3.2.4

1. ggplot(data = mpg) 을 μ‹€ν–‰ν•˜λΌ. μ–΄λ–€ 것이 λ‚˜μ˜€λŠ”κ°€?

ggplot(data=mpg)

ggplot ν•¨μˆ˜λ§ŒμœΌλ‘œλŠ” κ·Έλž˜ν”„λ₯Ό 그릴 수 μ—†κΈ° λ•Œλ¬Έμ— 아무것도 λ‚˜μ˜€μ§€ μ•ŠλŠ”λ‹€.
ggplot은 κ·Έλž˜ν”„λ₯Ό 그리기 μœ„ν•œ κΈ°λ³Έ 틀을 생성할 뿐, 데이터λ₯Ό μ–΄λ–€ λ°©μ‹μœΌλ‘œ μ‹œκ°ν™”ν• μ§€μ— λŒ€ν•œ 좔가적인 정보(예: aes, geom_point, geom_line λ“±)κ°€ ν•„μš”ν•˜λ‹€.

2.mpg 에 행이 λͺ‡κ°œ μžˆλŠ”κ°€? 열은 λͺ‡κ°œμΈκ°€?

dim(mpg)
[1] 234  11

dim()을 μ‚¬μš©ν•˜λ©΄ mpg λ°μ΄ν„°μ…‹μ˜ ν–‰(row)κ³Ό μ—΄(column) 개수λ₯Ό 확인할 수 μžˆλ‹€.

ν–‰: mpg λ°μ΄ν„°μ…‹μ—λŠ” 총 234개의 행이 μžˆλ‹€.
μ—΄: mpg λ°μ΄ν„°μ…‹μ—λŠ” 총 11개의 열이 μžˆλ‹€.

3. drv λ³€μˆ˜λŠ” 무엇을 μ˜λ―Έν•˜λŠ”κ°€? 이λ₯Ό μ•Œμ•„λ³΄κΈ° μœ„ν•΄ ?mpg 을 μ‹€ν–‰ν•˜μ—¬ 도움말을 읽으라.

?mpg

drv
the type of drive train, where f = front-wheel drive, r = rear wheel drive, 4 = 4wd

drv λ³€μˆ˜λŠ” μžλ™μ°¨μ˜ ꡬ동 방식을 λ‚˜νƒ€λ‚Έλ‹€.
fλŠ” μ „λ₯œκ΅¬λ™(front-wheel drive),
rλŠ” ν›„λ₯œκ΅¬λ™(rear-wheel drive),
4λŠ” 사λ₯œκ΅¬λ™(four-wheel drive)을 μ˜λ―Έν•œλ‹€.

4. hwy vs cyl 산점도λ₯Ό 그리라.

ggplot(data = mpg, aes(x = hwy, y = cyl)) + geom_point()


5. class vs drv 산점도λ₯Ό 그리면 μ–΄λ–»κ²Œ λ˜λŠ”κ°€? 이 ν”Œλ‘―μ€ μ™œ μ“Έλͺ¨κ°€ μ—†λŠ”κ°€?

ggplot(data = mpg, aes(x = class, y = drv)) + geom_point()

class와 drv λ³€μˆ˜λŠ” μ°¨λŸ‰μ’…λ₯˜μ™€ κ΅¬λ™λ°©μ‹μ΄λΌλŠ” μ˜΅μ…˜ 정보듀 μ •λ„λ§Œ λ‚˜νƒ€λ‚΄λŠ” μˆ«μžκ°€ μ•„λ‹Œ λ²”μ£Όν˜• 데이터이닀.
λ”°λΌμ„œ μΆ”μ„Έλ‚˜ 관계λ₯Ό νŒŒμ•…ν•˜λŠ”λ°μ—λŠ” 산점도λ₯Ό μ΄μš©ν•œ plot이 μ ν•©ν•˜μ§€ μ•Šλ‹€κ³  보인닀.

3.3.1

1. What’s gone wrong with this code? Why are the points not blue?

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy, color = "blue"))

μ½”λ“œμ—μ„œ β€œblue”가 λ²”μ£Όν˜• κ°’μœΌλ‘œ μΈμ‹λ˜μ–΄ μƒ‰μƒμœΌλ‘œ μ μš©λ˜μ§€ μ•Šμ€ 것이이닀.
β€œblue”λ₯Ό λ³€μˆ˜λ‘œ κ°„μ£Όν•˜μ§€ μ•Šκ³  κ³ μ •λœ μƒ‰μƒμœΌλ‘œ μ„€μ •ν•˜λ €λ©΄, aes() ν•¨μˆ˜ 밖에 color = β€œblue”λ₯Ό μ§€μ •ν•΄μ•Ό ν•œλ‹€.

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy), color = "blue")

μ΄λ ‡κ²Œ ν•˜λ©΄ λͺ¨λ“  점이 νŒŒλž€μƒ‰μœΌλ‘œ ν‘œμ‹œλ˜λ©°, λ²”μ£Όν˜• κ°’μœΌλ‘œ 잘λͺ» λ§€ν•‘λ˜μ§€ μ•ŠλŠ”λ‹€.

2. Which variables in mpg are categorical? Which variables are continuous? (Hint: type ?mpg to read the documentation for the dataset). How can you see this information when you run mpg?

?mpg
str(mpg)
tibble [234 Γ— 11] (S3: tbl_df/tbl/data.frame)
 $ manufacturer: chr [1:234] "audi" "audi" "audi" "audi" ...
 $ model       : chr [1:234] "a4" "a4" "a4" "a4" ...
 $ displ       : num [1:234] 1.8 1.8 2 2 2.8 2.8 3.1 1.8 1.8 2 ...
 $ year        : int [1:234] 1999 1999 2008 2008 1999 1999 2008 1999 1999 2008 ...
 $ cyl         : int [1:234] 4 4 4 4 6 6 6 4 4 4 ...
 $ trans       : chr [1:234] "auto(l5)" "manual(m5)" "manual(m6)" "auto(av)" ...
 $ drv         : chr [1:234] "f" "f" "f" "f" ...
 $ cty         : int [1:234] 18 21 20 21 16 18 18 18 16 20 ...
 $ hwy         : int [1:234] 29 29 31 30 26 26 27 26 25 28 ...
 $ fl          : chr [1:234] "p" "p" "p" "p" ...
 $ class       : chr [1:234] "compact" "compact" "compact" "compact" ...

λ²”μ£Όν˜• λ³€μˆ˜: manufacturer, model, trans, drv, fl, class
μ—°μ†ν˜• λ³€μˆ˜: displ, year, cyl, cty, hwy

?mpgλ₯Ό μ‹€ν–‰ν•΄ 각 λ³€μˆ˜μ˜ μ„€λͺ…을 λ³Ό 수 있고, str(mpg)λ₯Ό μ‹€ν–‰ν•΄ 각 λ³€μˆ˜μ— λŒ€ν•œ μœ ν˜•μ„ 확인해볼 수 μžˆλ‹€.
이 λ•Œ factor둜 ν‘œμ‹œλœ λ³€μˆ˜λŠ” λ²”μ£Όν˜•, numμ΄λ‚˜ int둜 ν‘œμ‹œλœ λ³€μˆ˜λŠ” μ—°μ†ν˜•μ΄λ‹€.

3. Map a continuous variable to color, size, and shape. How do these aesthetics behave differently for categorical vs.Β continuous variables?

ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy, color = hwy, size = hwy))

μ—°μ†ν˜• λ³€μˆ˜λ₯Ό 색상(color)μ΄λ‚˜ 크기(size)에 λ§€ν•‘ν•˜λ©΄ 색상은 κ·ΈλΌλ°μ΄μ…˜μœΌλ‘œ, ν¬κΈ°λŠ” 값에 따라 λΉ„λ‘€μ μœΌλ‘œ μ»€μ§€κ±°λ‚˜ μž‘μ•„μ§„λ‹€.

ggplot(data = mpg, aes(x = displ, y = hwy, color = hwy, size = hwy, shape = hwy)) + 
  geom_point()

ν•˜μ§€λ§Œ μ—°μ†ν˜• λ³€μˆ˜λ₯Ό λͺ¨μ–‘(shape)에 λ§€ν•‘ν•˜λ©΄ μœ„μ™€ 같이 였λ₯˜κ°€ λ°œμƒν•œλ‹€.
λͺ¨μ–‘은 보톡 λ²”μ£Όν˜• 데이터λ₯Ό μœ„ν•΄ μ„€κ³„λ˜μ–΄, 값에 따라 κ³ μœ ν•œ λͺ¨μ–‘을 μ§€μ •ν•  수 μ—†κΈ° λ•Œλ¬Έμ΄λ‹€.

ggplot(data = mpg, aes(x = displ, y = hwy, color = class, size = class, shape = class)) + 
  geom_point()

μœ„μ™€ 같이 λ²”μ£Όν˜• λ³€μˆ˜λŠ” color와 shape에 맀핑이 μ ν•©ν•˜λ©°, μƒ‰μƒμ΄λ‚˜ λͺ¨μ–‘을 λ²”μ£Όλ³„λ‘œ λ‹€λ₯΄κ²Œ μ§€μ •ν•  수 μžˆλ‹€.
size에 λ§€ν•‘ν•˜λ©΄ μ˜λ―Έκ°€ λͺ¨ν˜Έν•΄μ§ˆ 수 μžˆλ‹€.

4. What happens if you map the same variable to multiple aesthetics?

같은 λ³€μˆ˜λ₯Ό μ—¬λŸ¬ 속성(예: 색상과 크기)에 λ§€ν•‘ν•˜λ©΄ ν•΄λ‹Ή λ³€μˆ˜μ˜ 변화에 따라 μ—¬λŸ¬ μ‹œκ°μ  μš”μ†Œκ°€ λ™μ‹œμ— 달라진닀.

ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy, color = hwy, size = hwy))

μœ„μ˜ 경우 hwy에 따라 색상과 크기가 λ™μ‹œμ— 바뀐닀.
이 방법은 λ³€μˆ˜κ°€ κ°•μ‘°λ˜μ§€λ§Œ 가독성이 λ–¨μ–΄μ§ˆ 수 μžˆλ‹€.

5. What does the stroke aesthetic do? What shapes does it work with? (Hint: use ?geom_point)

?geom_point

ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy), shape = 21, color = "black", fill = "blue", stroke = 2)

stroke 속성은 ggplot2μ—μ„œ μ™Έκ³½μ„  λ‘κ»˜λ₯Ό μ‘°μ •ν•˜λŠ” 역할을 ν•œλ‹€.
특히, 채움색과 μ™Έκ³½μ„  색을 각각 μ„€μ •ν•  수 μžˆλŠ” λ„ν˜•(21번~24번)μ—λ§Œ μ μš©λœλ‹€.

6. What happens if you map an aesthetic to something other than a variable name, like aes(colour = displ < 5)? Note, you’ll also need to specify x and y.

ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy, color = displ < 5))

displ < 5처럼 쑰건문을 μ‚¬μš©ν•΄ 색상을 λ§€ν•‘ν•˜λ©΄, TRUE/FALSE λ…Όλ¦¬κ°’μœΌλ‘œ ν‘œμ‹œλœλ‹€.

3.5.1

1. What happens if you facet on a continuous variable?

각 고유 값에 λŒ€ν•΄ λ³„λ„μ˜ νŒ¨λ„μ΄ λ§Œλ“€μ–΄μ§„λ‹€. μ—°μ†ν˜• λ³€μˆ˜μ— 고유 값이 λ§Žμ„ 경우 νŒ¨λ„μ΄ λ„ˆλ¬΄ λ§Žμ•„μ Έ, κ·Έλž˜ν”„κ°€ λ³΅μž‘ν•˜κ³  ν•΄μ„ν•˜κΈ° μ–΄λ €μ›Œμ§ˆ 수 μžˆλ‹€. facet은 일반적으둜 λ²”μ£Όν˜• λ³€μˆ˜μ™€ ν•¨κ»˜ μ‚¬μš©ν•  λ•Œ κ°€μž₯ μœ μš©ν•˜λ‹€.

2. What do the empty cells in plot with facet_grid(drv ~ cyl) mean? How do they relate to this plot?

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = drv, y = cyl))

빈 셀은 drv와 cyl의 νŠΉμ • 쑰합이 데이터에 쑴재 ν•˜μ§€ μ•ŠλŠ”λ‹€λŠ” 것을 μ˜λ―Έν•œλ‹€.
μœ„μ˜ 경우 예λ₯Ό λ“€μ–΄, drv=4일 λ•Œ cyl=5인 쑰합은 μ—†λ‹€.

3. What plots does the following code make? What does . do?

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy)) +
  facet_grid(drv ~ .)

μœ„μ˜ 경우, drv λ³€μˆ˜λ₯Ό ν–‰(row) λ°©ν–₯으둜 fecetν•œλ‹€.
각 drv κ°’λ§ˆλ‹€ 행이 λ§Œλ“€μ–΄μ§€κ³ , displκ³Ό hwy λ³€μˆ˜μ˜ 산점도가 각 facet에 λ‚˜νƒ€λ‚œλ‹€.
μ—¬κΈ°μ„œ .λŠ” ν•΄λ‹Ή λ°©ν–₯(μ—΄)을 λΉ„μ›Œλ‘”λ‹€λŠ” 의미둜 facet을 ν–‰ λ°©ν–₯으둜만 λ‚˜λˆŒ λ•Œ μ‚¬μš©ν•œλ‹€.

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy)) +
  facet_grid(. ~ cyl)

μœ„μ˜ 경우, cyl λ³€μˆ˜λ₯Ό μ—΄(column) λ°©ν–₯으둜 facetν•˜μ—¬ 각 cyl κ°’λ§ˆλ‹€ 열이 λ§Œλ“€μ–΄ μ§„λ‹€.
μ—¬κΈ°μ„œ .λŠ” ν•΄λ‹Ή λ°©ν–₯(ν–‰)을 λΉ„μ›Œλ‘”λ‹€λŠ” 의미둜 facet을 μ—΄ λ°©ν–₯으둜만 λ‚˜λˆŒ λ•Œ μ‚¬μš©ν•œλ‹€.

4. Take the first faceted plot in this section:

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy)) + 
  facet_wrap(~ class, nrow = 2)

What are the advantages to using faceting instead of the colour aesthetic? What are the disadvantages? How might the balance change if you had a larger dataset?


μž₯점:

facet_wrap은 νŒ¨λ„μ„ μ—¬λŸ¬ ν–‰κ³Ό 열에 μžλ™μœΌλ‘œ λ°°μΉ˜ν•˜μ—¬, νŠΉμ • λ³€μˆ˜μ˜ 각 값에 λŒ€ν•΄ νŒ¨λ„μ„ ꡬ성할 수 μžˆμ–΄ μ‹œκ°μ μœΌλ‘œ κΉ”λ”ν•˜λ‹€.  

색상(color)을 μ‚¬μš©ν•˜μ—¬ λ³€μˆ˜λ³„λ‘œ κ΅¬λΆ„ν•˜λŠ” λŒ€μ‹ , facet을 μ‚¬μš©ν•˜λ©΄ 더 λͺ…ν™•ν•˜κ²Œ 비ꡐ할 수 μžˆλ‹€. 

단점:

facet의 κ°œμˆ˜κ°€ λ§Žμ•„μ§ˆ 경우 전체 plot이 λ³΅μž‘ν•΄μ§ˆ 수 있고, 특히 데이터가 큰 경우 ν™”λ©΄ 곡간을 많이 μ°¨μ§€ν•  수 μžˆλ‹€.  

νŒ¨λ„λ‘œ λ‚˜λ‰œ 경우, 전체적인 데이터 뢄포λ₯Ό λ™μ‹œμ— λΉ„κ΅ν•˜κΈ° μ–΄λ €μšΈ 수 μžˆλ‹€.  

데이터 셋이 더 컀질 경우:

데이터가 더 컀진닀면 색상 ꡬ뢄이 더 효과적일 수 μžˆλ‹€. facet은 μ λ‹Ήν•œ λ²”μ£Όμ˜ 데이터 μ…‹μ—μ„œ 더 μœ μš©ν•  것이닀.  

5. Read ?facet_wrap. What does nrow do? What does ncol do? What other options control the layout of the individual panels? Why doesn’t facet_grid() have nrow and ncol arguments?

?facet_wrap

facet_wrapμ—μ„œ nrowλŠ” ν–‰(row)의 개수λ₯Ό μ§€μ •ν•˜κ³ , ncol은 μ—΄(column)의 개수λ₯Ό μ§€μ •ν•œλ‹€. 이 μ˜΅μ…˜μ„ 톡해 νŒ¨λ„μ˜ 배치λ₯Ό μ‘°μ •ν•  수 μžˆλ‹€.
μΆ”κ°€λ‘œ scales μ˜΅μ…˜μ„ μ‚¬μš©ν•˜μ—¬ 각 νŒ¨λ„μ˜ μΆ• λ²”μœ„λ₯Ό μ‘°μ •ν•  수 μžˆλ‹€.


facet_gridλŠ” ν–‰κ³Ό μ—΄λ‘œ κ³ μ •λœ 격자 ν˜•νƒœλ‘œ facet을 λ§Œλ“€κΈ° λ•Œλ¬Έμ— nrow와 ncol μ˜΅μ…˜μ΄ μ—†λ‹€. facet_gridλŠ” 일반적으둜 두 개의 λ²”μ£Όν˜• λ³€μˆ˜ κ°„ 관계λ₯Ό μ‹œκ°ν™”ν•  λ•Œ μ‚¬μš©ν•œλ‹€.


6. Which of the following two plots makes it easier to compare engine size (displ) across cars with different drive trains? What does this say about when to place a faceting variable across rows or columns?

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy)) + 
  facet_grid(drv ~ .)

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy)) + 
  facet_grid(. ~ drv)

첫 번째 plot은 drvλ₯Ό ν–‰ λ°©ν–₯으둜 facetν•˜κ³ , 두 번째 ν”Œλ‘―μ€ drvλ₯Ό μ—΄ λ°©ν–₯으둜 facetν•œλ‹€.
두 plot μ€‘μ—λŠ” 첫번째 plot이 λΉ„κ΅ν•˜κΈ° 더 쉽닀.
첫 번째 plotμ—μ„œλŠ” drv λ³€μˆ˜μ— 따라 ν–‰ λ°©ν–₯으둜 νŒ¨λ„μ΄ λ‚˜λ‰˜μ–΄ 각 ꡬ동 방식별 데이터가 ν•œ 행에 μ •λ ¬λ˜λ―€λ‘œ, μœ„μ•„λž˜λ‘œ λΉ„κ΅ν•˜κΈ°κ°€ 더 직관적이닀. 즉, 각 ꡬ동 λ°©μ‹μ˜ μ—”μ§„ 크기 뢄포λ₯Ό 수직으둜 λ°°μΉ˜ν•˜μ—¬ λΉ λ₯΄κ²Œ 비ꡐ할 수 μžˆλ‹€.

반면, 두 번째 plot은 drvλ₯Ό μ—΄ λ°©ν–₯으둜 νŒ¨λ„μ„ λ‚˜λˆ΄μœΌλ―€λ‘œ, 쒌우둜 비ꡐ해야 ν•œλ‹€. 이 경우, μ‹œκ°μ μœΌλ‘œ λΉ„κ΅ν•˜λŠ” 데 더 λ§Žμ€ 이동이 ν•„μš”ν•΄ μ΄ν•΄ν•˜κΈ°κ°€ λΆˆνŽΈν•  수 μžˆλ‹€.

μœ„μ˜ μ˜ˆμ‹œλŠ” λΉ„κ΅ν•˜λ €λŠ” λ³€μˆ˜λ₯Ό ν–‰ λ°©ν–₯으둜 λ°°μΉ˜ν•˜λŠ” 것이 μ§κ΄€μ μ΄λΌλŠ” 것을 보여쀀닀. ν•˜μ§€λ§Œ facetλ³€μˆ˜κ°€ λ§Žκ±°λ‚˜ κ°€λ‘œλ‘œ λ‚˜μ—΄ν•˜λŠ” 것이 더 μ ν•©ν•œ κ²½μš°λŠ” μ—΄λ°©ν–₯으둜 λ°°μΉ˜ν•  μˆ˜λ„ μžˆλ‹€. λ”°λΌμ„œ λ³€μˆ˜λ₯Ό ν–‰κ³Ό μ—΄ 쀑 μ–΄λŠ λ°©ν–₯으둜 λ°°μΉ˜ν• μ§€λŠ” λΉ„κ΅ν•˜λ €λŠ” λ°μ΄ν„°μ˜ νŠΉμ„±κ³Ό μ‹œκ°μ μœΌλ‘œ 비ꡐ가 더 μ‰¬μš΄ λ°©ν–₯을 κ³ λ €ν•˜μ—¬ κ²°μ •ν•΄μ•Ό ν•œλ‹€.

7. Recreate this plot using facet_wrap() instead of facet_grid(). How do the positions of the facet labels change?

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy)) +
  facet_grid(drv ~ .)

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy)) +
  facet_wrap(~ drv)

μœ„μ˜ 경우, facet_wrap()은 νŒ¨λ„μ„ μžλ™μœΌλ‘œ λ°°μΉ˜ν•˜λ©°, 각 νŒ¨λ„μ˜ 라벨 μœ„μΉ˜κ°€ μœ„μͺ½ 쀑앙에 λ‚˜νƒ€λ‚œλ‹€.
facet_grid(drv ~ .)μ—μ„œμ™€ 달리 νŒ¨λ„μ΄ ν–‰ λ˜λŠ” μ—΄λ‘œλ§Œ λ°°μΉ˜λ˜μ§€ μ•Šκ³ , μ‚¬μš©μžκ°€ μ§€μ •ν•œ nrow λ˜λŠ” ncol에 따라 νŒ¨λ„ 배치λ₯Ό μ‘°μ •ν•  수 μžˆλ‹€.

3.6.1

1. What geom would you use to draw a line chart? A boxplot? A histogram? An area chart?

μ„  κ·Έλž˜ν”„ (Line chart): geom_line()
λ°•μŠ€ ν”Œλ‘― (Boxplot): geom_boxplot()
νžˆμŠ€ν† κ·Έλž¨ (Histogram): geom_histogram()
면적 κ·Έλž˜ν”„ (Area chart): geom_area()

2. Run this code in your head and predict what the output will look like. Then, run the code in R and check your predictions.

각 drvλ³„λ‘œ λ‹€λ₯Έ μƒ‰μƒμ˜ 점이 λ‚˜νƒ€λ‚˜κ³ , 각 색상에 ν•΄λ‹Ήν•˜λŠ” 곑선이 좔가될 것이닀.
se = FALSEλŠ” μ‹ λ’° ꡬ간을 ν‘œμ‹œν•˜μ§€ μ•Šλ„λ‘ μ„€μ •ν•΄ smmoth 곑선에 μ‹ λ’° ꡬ간 음영이 없을 것이닀.

ggplot(data = mpg, mapping = aes(x = displ, y = hwy, color = drv)) + 
  geom_point() + 
  geom_smooth(se = FALSE)

3. What does show.legend = FALSE do? What happens if you remove it? Why do you think I used it earlier in the chapter?

show.legend = FALSEλŠ” λ²”λ‘€(legend)λ₯Ό μˆ¨κΈ΄λ‹€.
이것을 μ œκ±°ν•˜λ©΄ λ²”λ‘€κ°€ λ‹€μ‹œ ν‘œμ‹œλœλ‹€.
μ±…μ—μ„œ 이λ₯Ό μ‚¬μš©ν•œ μ΄μœ λŠ” μ‹œκ°ν™”μ—μ„œ λ²”λ‘€κ°€ ν•„μš”ν•˜μ§€ μ•Šλ‹€κ³  νŒλ‹¨ν–ˆκ±°λ‚˜ μ‹œκ°μ„ λ‹¨μˆœν™”ν•˜κΈ° μœ„ν•΄μ„œμΌ 것이닀.

4. What does the se argument to geom_smooth() do?

se = TRUE둜 μ„€μ •ν•˜λ©΄ smmoth 곑선 μ£Όμœ„μ— μ‹ λ’° ꡬ간을 λ‚˜νƒ€λ‚΄λŠ” 음영이 μΆ”κ°€λœλ‹€.
기본값은 TRUE이고, se = FALSE둜 μ„€μ •ν•˜λ©΄ μ‹ λ’° ꡬ간을 μ œκ±°ν•˜μ—¬ κ³‘μ„ λ§Œ ν‘œμ‹œλœλ‹€.

5. Will these two graphs look different? Why/why not?

ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) + 
  geom_point() + 
  geom_smooth()

ggplot() + 
  geom_point(data = mpg, mapping = aes(x = displ, y = hwy)) + 
  geom_smooth(data = mpg, mapping = aes(x = displ, y = hwy))

두 κ·Έλž˜ν”„λŠ” λ™μΌν•˜κ²Œ 보일 것이닀. μ™œλƒν•˜λ©΄ 두 μ½”λ“œ λͺ¨λ‘ λ™μΌν•œ 데이터λ₯Ό μ‚¬μš©ν•˜μ—¬ 같은 산점도와 smooth 곑선을 그리기 λ•Œλ¬Έμ΄λ‹€.
첫 번째 μ½”λ“œμ—μ„œλŠ” 데이터와 맀핑을 ggplot() ν•¨μˆ˜μ—μ„œ μ„€μ •ν–ˆμ§€λ§Œ, 두 번째 μ½”λ“œμ—μ„œλŠ” 각각의 geom ν•¨μˆ˜μ—μ„œ μ„€μ •ν–ˆμŠ΅λ‹ˆλ‹€. μ‹œκ°μ μœΌλ‘œλŠ” 차이가 μ—†μœΌλ‚˜, 두 번째 방법이 더 λͺ…μ‹œμ μ΄λΌκ³  ν•  수 μžˆλ‹€.

6. Recreate the R code necessary to generate the following graphs. Note that wherever a categorical variable is used in the plot, it’s drv.

ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) + 
  geom_point(color = "black") + 
  geom_smooth(se = FALSE)

ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) + 
  geom_point(color = "black") + 
  geom_smooth(aes(group = drv), color = "blue", se = FALSE)

ggplot(data = mpg, mapping = aes(x = displ, y = hwy, color = drv)) + 
  geom_point() + 
  geom_smooth(se = FALSE)

ggplot(data = mpg, mapping = aes(x = displ, y = hwy, color = drv)) + 
  geom_point() + 
  geom_smooth(se = FALSE, color = "blue")

ggplot(data = mpg, mapping = aes(x = displ, y = hwy, color = drv)) + 
  geom_point() + 
  geom_smooth(aes(linetype = drv), se = FALSE)

ggplot(data = mpg, mapping = aes(x = displ, y = hwy, fill = drv)) + 
  geom_point(size = 4, shape = 21, color = "white", stroke = 1.5)

3.7.1

1. What is the default geom associated with stat_summary()? How could you rewrite the previous plot to use that geom function instead of the stat function?

stat_summary()의 κΈ°λ³Έ geom은 geom_pointrange()이닀. stat_summary()λŠ” μš”μ•½ 톡계λ₯Ό 그릴 λ•Œ μ‚¬μš©λ˜λ©°, 주둜 평균과 μ‹ λ’° ꡬ간 등을 ν‘œμ‹œν•  λ•Œ μœ μš©ν•˜λ‹€.
stat_summary() λŒ€μ‹  κΈ°λ³Έ geom을 μ‚¬μš©ν•˜κ³  μ‹Άλ‹€λ©΄ geom_pointrange()λ₯Ό μ‚¬μš©ν•  수 μžˆλ‹€.

ggplot(data = diamonds) +  
  geom_pointrange(
    mapping = aes(x = cut, y = depth, ymin = ..ymin.., ymax = ..ymax..),
    stat = "summary",
    fun.min = min,
    fun.max = max,
    fun = median
  )

ggplot(data = diamonds) +
  geom_pointrange(mapping = aes (x = cut, y = depth, ymin =depth, ymax =depth))

2. What does geom_col() do? How is it different to geom_bar()?

geom_col()은 xμΆ•κ³Ό yμΆ•μ˜ 값을 κ·ΈλŒ€λ‘œ μ‚¬μš©ν•΄ λ§‰λŒ€ κ·Έλž˜ν”„λ₯Ό κ·Έλ¦°λ‹€. 즉, 데이터 ν”„λ ˆμž„ 내에 이미 yμΆ• 값이 μžˆλŠ” 경우 geom_col()을 μ‚¬μš©ν•˜μ—¬ κ·Έ 값을 κ·ΈλŒ€λ‘œ λ§‰λŒ€μ˜ λ†’μ΄λ‘œ λ‚˜νƒ€λ‚Έλ‹€.


geom_bar()λŠ” y값이 μ£Όμ–΄μ§€μ§€ μ•ŠμœΌλ©΄ μžλ™μœΌλ‘œ λ°μ΄ν„°μ˜ 개수λ₯Ό μ„Έμ–΄ λΉˆλ„ λ§‰λŒ€ κ·Έλž˜ν”„λ₯Ό κ·Έλ¦°λ‹€. 즉, geom_bar()λŠ” stat = β€œcount”가 κΈ°λ³Έ 섀정이며, λ³„λ„μ˜ y값이 ν•„μš”ν•˜μ§€ μ•Šλ‹€.


geom_col()은 yμΆ• 값을 κ·ΈλŒ€λ‘œ μ‚¬μš©ν•˜κ³ , geom_bar()λŠ” yμΆ• 값을 λ”°λ‘œ μ§€μ •ν•˜μ§€ μ•ŠμœΌλ©΄ μžλ™μœΌλ‘œ 개수λ₯Ό μ„Όλ‹€.


3. Most geoms and stats come in pairs that are almost always used in concert. Read through the documentation and make a list of all the pairs. What do they have in common?

λŒ€λΆ€λΆ„μ˜ geomκ³Ό stat은 pair을 이루며, λ™μΌν•œ 데이터 λ³€ν™˜κ³Ό μ‹œκ°ν™” λͺ©μ μ„ κ°€μ§€κ³  μžˆλ‹€. 예λ₯Ό λ“€μ–΄, geom_bar()λŠ” stat_count()와 ν•¨κ»˜ μ‚¬μš©λ˜λ©°, geom_smooth()λŠ” stat_smooth()와 ν•¨κ»˜ μ‚¬μš©λœλ‹€.
μ£Όμš”μ˜ˆμ‹œ:

geom_bar() ↔ stat_count()  
geom_col() ↔ stat_identity()
geom_histogram() ↔ stat_bin()
geom_density() ↔ stat_density()
geom_boxplot() ↔ stat_boxplot()
geom_smooth() ↔ stat_smooth()
geom_point() ↔ stat_identity()
geom_line() ↔ stat_identity()
geom_violin() ↔ stat_ydensity()
geom_pointrange() ↔ stat_summary()
geom_errorbar() ↔ stat_summary()
geom_ribbon() ↔ stat_summary()

http://ggplot2.tidyverse.org/reference/ ν•΄λ‹Ή 링크λ₯Ό μ°Έκ³ ν•  수 μžˆλ‹€.


4. What variables does stat_smooth() compute? What parameters control its behaviour?

stat_smooth()의 μ£Όμš” variables:

y
ymin
ymax

μ£Όμš” parameter:

method: smoothν•¨μˆ˜λ₯Ό μ„ νƒν•˜λŠ” μ˜΅μ…˜
se: μ‹ λ’° ꡬ간을 ν‘œμ‹œν• μ§€ μ—¬λΆ€λ₯Ό κ²°μ •ν•˜λ©°, TRUE λ˜λŠ” FALSE둜 μ„€μ •
level: μ‹ λ’° κ΅¬κ°„μ˜ μˆ˜μ€€μ„ μ„€μ •ν•©λ‹ˆλ‹€ (예: 0.95λŠ” 95% μ‹ λ’° ꡬ간).
span: method = "loess"일 λ•Œ κ³‘μ„ μ˜ smoothness을 μ œμ–΄.
ggplot(data = mpg, aes(x = displ, y = hwy)) +
  geom_point(aes(color = drv)) +
  stat_smooth(se = FALSE)

5. In our proportion bar chart, we need to set group = 1. Why? In other words what is the problem with these two graphs?

ggplot(data = diamonds) + 
  geom_bar(mapping = aes(x = cut, y = after_stat(prop)))

ggplot(data = diamonds) + 
  geom_bar(mapping = aes(x = cut, fill = color, y = after_stat(prop)))

λΉ„μœ¨ λ§‰λŒ€ κ·Έλž˜ν”„μ—μ„œ group = 1을 μ„€μ •ν•˜λŠ” μ΄μœ λŠ” xμΆ•μ˜ 각 λ²”μ£Όκ°€ 독립적인 그룹으둜 κ°„μ£Όλ˜λ„λ‘ ν•˜κΈ° μœ„ν•¨μ΄λ‹€. group = 1을 μ„€μ •ν•˜μ§€ μ•ŠμœΌλ©΄ 각 λ²”μ£Όκ°€ κ°œλ³„μ μœΌλ‘œ κ³„μ‚°λ˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— μ›ν•˜λŠ” λΉ„μœ¨μ΄ λ‚˜νƒ€λ‚˜μ§€ μ•Šκ²Œ λœλ‹€.

μœ„ 두 κ·Έλž˜ν”„μ˜ λ¬Έμ œμ μ€ λΉ„μœ¨μ΄ μ œλŒ€λ‘œ κ³„μ‚°λ˜μ§€ μ•ŠλŠ”λ‹€λŠ” 것이이닀.이 두 κ·Έλž˜ν”„μ—μ„œλŠ” group = 1을 μ„€μ •ν•˜μ§€ μ•Šμ•„ 각 cut의 값듀이 λ³„λ„λ‘œ κ·Έλ£Ήν™”λ˜μ§€ μ•Šμ•˜λ‹€. 결과적으둜 prop이 전체 데이터λ₯Ό λŒ€μƒμœΌλ‘œ κ³„μ‚°λ˜μ–΄, 각 cut의 λΉ„μœ¨μ΄ μ˜¬λ°”λ₯΄κ²Œ ν‘œμ‹œλ˜μ§€ μ•ŠλŠ”λŠ”λ‹€. group = 1을 μΆ”κ°€ν•˜μ—¬ 각 cut λ²”μ£Όκ°€ 독립적인 그룹으둜 κ³„μ‚°λ˜λ„λ‘ ν•΄μ•Ό μ˜¬λ°”λ₯Έ λΉ„μœ¨μ΄ ν‘œμ‹œλœλ‹€.

3.8.1

1. What is the problem with this plot? How could you improve it?

ggplot(data = mpg, mapping = aes(x = cty, y = hwy)) + 
  geom_point()
문제점: μ—¬λŸ¬ 데이터 ν¬μΈνŠΈκ°€ λ™μΌν•œ μœ„μΉ˜μ— μžˆμ–΄μ„œ 겹치기 λ•Œλ¬Έμ— λͺ¨λ“  데이터λ₯Ό 확인할 수 μ—†λ‹€. 겹친 점듀이 λ§Žμ•„ μ‹€μ œ 데이터보닀 적은 점듀이 보이게 λœλ‹€.
κ°œμ„ λ°©λ²•: geom_jitter()λ₯Ό μ‚¬μš©ν•˜μ—¬ 점듀을 μ•½κ°„μ”© ν©μ–΄μ§€κ²Œ ν•˜λ©΄ κ²ΉμΉ˜λŠ” 문제λ₯Ό 쀄이고 데이터λ₯Ό 더 잘 μ‹œκ°ν™”ν•  수 μžˆλ‹€.  
ggplot(data = mpg, mapping = aes(x = cty, y = hwy)) +
  geom_jitter()

2. What parameters to geom_jitter() control the amount of jittering?

geom_jitter()μ—μ„œ 점듀이 ν©μ–΄μ§€λŠ” μ •λ„λŠ” width와 height νŒŒλΌλ―Έν„°λ‘œ μ œμ–΄ν•  수 μžˆλ‹€.

ggplot(data = mpg, mapping = aes(x = cty, y = hwy)) + 
  geom_jitter(width = 0.2, height = 0.2)

3. Compare and contrast geom_jitter() with geom_count()

# geom jitter
ggplot(data = mpg, mapping = aes(x = cty, y = hwy)) +
  geom_jitter()

# geom count
ggplot(data = mpg, mapping = aes(x = cty, y = hwy)) +
  geom_count()

geom_jitter(): 점듀을 μ•½κ°„μ”© ν©μ–΄μ§€κ²Œ ν•˜μ—¬ μ‹œκ°ν™”ν•œλ‹€. 각 데이터 ν¬μΈνŠΈλŠ” μ•½κ°„μ˜ λ³€μœ„κ°€ μ μš©λ˜μ–΄ ν‘œμ‹œλœλ‹€.
geom_count(): λ™μΌν•œ μœ„μΉ˜μ— μ—¬λŸ¬ 데이터 ν¬μΈνŠΈκ°€ μžˆμ„ 경우, 점의 크기λ₯Ό κ·Έ κ°œμˆ˜μ— λΉ„λ‘€ν•˜μ—¬ ν‘œμ‹œν•œλ‹€. 같은 μœ„μΉ˜μ— λ§Žμ€ 데이터가 μžˆμ„μˆ˜λ‘ 점이 컀진닀.  

즉, geom_jitter()λŠ” 점듀을 ν©μ–΄μ§€κ²Œν•΄ 데이터λ₯Ό λΆ„λ¦¬ν•˜μ§€λ§Œ, geom_count()λŠ” 점의 크기둜 λ°μ΄ν„°μ˜ 쀑볡을 ν‘œν˜„ν•œλ‹€.

4. What’s the default position adjustment for geom_boxplot()? Create a visualisation of the mpg dataset that demonstrates it.

geom_boxplot()의 κΈ°λ³Έ μœ„μΉ˜ μ‘°μ • 방식은 position = β€œdodge”이닀. 이 μœ„μΉ˜ μ‘°μ • 방식은 각 범주에 λŒ€ν•΄ λ°•μŠ€ ν”Œλ‘―μ„ λ‚˜λž€νžˆ λ°°μΉ˜ν•˜μ—¬ μ„œλ‘œ κ²ΉμΉ˜μ§€ μ•Šλ„λ‘ ν•œλ‹€.

ggplot(data = mpg, mapping = aes(x = drv, y = hwy, fill = drv)) + 
  geom_boxplot()

3.9.1

1. Turn a stacked bar chart into a pie chart using coord_polar().

ggplot(data = diamonds, aes(x = factor(1), fill = cut)) +
  geom_bar(width = 1) +
  coord_polar(theta = "y") +
  labs(x = NULL, y = NULL)

2. What does labs() do? Read the documentation.

labs() λŠ” κ·Έλž˜ν”„μ˜ λ ˆμ΄λΈ”μ„ μ„€μ •ν•˜λŠ” ν•¨μˆ˜λ‘œ, μΆ• 제λͺ©, κ·Έλž˜ν”„ 제λͺ©, λ²”λ‘€ 제λͺ© 등을 λ³€κ²½ν•  수 μžˆλ‹€.
labs()λŠ” title, subtitle, caption, x, y, fill, color λ“±μ˜ λ§€κ°œλ³€μˆ˜λ₯Ό μ‚¬μš©ν•΄ κ·Έλž˜ν”„μ˜ λ‹€μ–‘ν•œ μš”μ†Œμ— 제λͺ©μ„ μΆ”κ°€ν•˜κ±°λ‚˜ λ³€κ²½ν•  수 μžˆλ‹€.

3. What’s the difference between coord_quickmap() and coord_map()?

coord_quickmap(): 지도λ₯Ό 그릴 λ•Œ 지리적 λΉ„μœ¨μ„ λΉ λ₯΄κ²Œ μ„€μ •ν•œλ‹€. 계산을 λ‹¨μˆœν™”ν•˜μ—¬ λΉ„μœ¨μ΄ μ •ν™•νžˆ λ§žμ§„ μ•Šμ§€λ§Œ, 속도가 λΉ λ₯΄λ‹€. 일반적으둜 지도가 화면에 μ μ ˆν•œ λΉ„μœ¨λ‘œ 보이도둝 μ‘°μ •ν•œλ‹€.직선이 μœ μ§€λ˜μ§€ μ•ŠλŠ”λ‹€.
coord_map(): 지리적 λΉ„μœ¨μ„ μ •ν™•ν•˜κ²Œ μ‘°μ •ν•˜λ©°, λ‹€μ–‘ν•œ νˆ¬μ˜λ²•μ„ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 계산이 더 λ³΅μž‘ν•˜κ³  μ‹œκ°„μ΄ 였래 κ±Έλ¦¬μ§€λ§Œ, 비ꡐ적 μ •ν™•ν•œ 지리적 λΉ„μœ¨μ„ μ œκ³΅ν•œλ‹€. 직선을 (λŒ€λΆ€λΆ„) μœ μ§€ν•œλ‹€.  


4. What does the plot below tell you about the relationship between city and highway mpg? Why is coord_fixed() important? What does geom_abline() do?

ggplot(data = mpg, mapping = aes(x = cty, y = hwy)) +
  geom_point() + 
  geom_abline() +
  coord_fixed()

이 plot은 cty(λ„μ‹œ μ—°λΉ„)와 hwy(κ³ μ†λ„λ‘œ μ—°λΉ„) 사이에 μ–‘μ˜ 상관 관계가 μžˆμŒμ„ 보여쀀닀. 즉, ctyκ°€ λ†’μ„μˆ˜λ‘ hwy도 λ†’μ•„μ§€λŠ” κ²½ν–₯이 μžˆλ‹€.

coord_fixed()의 μ€‘μš”μ„±: coord_fixed()λŠ” xμΆ•κ³Ό yμΆ•μ˜ λΉ„μœ¨μ„ 1:1둜 κ³ μ •ν•˜μ—¬, 두 μΆ•μ˜ λ‹¨μœ„κ°€ λ™μΌν•œ λΉ„μœ¨λ‘œ ν‘œμ‹œλ˜λ„λ‘ ν•œλ‹€. 이λ₯Ό 톡해 geom_abline()μ—μ„œ μΆ”κ°€ν•œ λŒ€κ°μ„ (기울기 1의 μ„ )이 μ •ν™•νžˆ 45도 κ°λ„λ‘œ λ‚˜νƒ€λ‚˜λ©°, cty와 hwy의 관계λ₯Ό 더 μ§κ΄€μ μœΌλ‘œ 이해할 수 μžˆλ‹€. λΉ„μœ¨μ΄ κ³ μ •λ˜μ§€ μ•ŠμœΌλ©΄, μΆ•μ˜ λΉ„μœ¨ 차이둜 인해 관계가 μ™œκ³‘λ  수 μžˆλ‹€.
geom_abline()의 μ—­ν• : geom_abline()은 기본적으둜 κΈ°μšΈκΈ°κ°€ 1이고 절편이 0인 λŒ€κ°μ„ μ„ μΆ”κ°€ν•˜μ—¬, x와 yκ°€ 같은 값일 λ•Œμ˜ 기쀀선을 λ‚˜νƒ€λ‚Έλ‹€. 이 선을 κΈ°μ€€μœΌλ‘œ 데이터λ₯Ό 비ꡐ할 수 있으며, 이 경우 cty와 hwy의 관계λ₯Ό ν‰κ°€ν•˜λŠ” 데 도움을 μ€€λ‹€.  


5.2.4

1. Find all flights that

Had an arrival delay of two or more hours

Flew to Houston (IAH or HOU)

Were operated by United, American, or Delta

Departed in summer (July, August, and September)

Arrived more than two hours late, but didn’t leave late

Were delayed by at least an hour, but made up over 30 minutes in flight

library(nycflights13)
library(dplyr)

flights %>%
  filter(arr_delay >= 120,  # Had an arrival delay of two or more hours
         dest %in% c("IAH", "HOU"),  # Flew to Houston (IAH or HOU)
         carrier %in% c("UA", "AA", "DL"),  # Were operated by United, American, or Delta
         month %in% c(7, 8, 9),  # Departed in summer (July, August, and September)
         arr_delay > dep_delay,  # Arrived more than two hours late, but didn’t leave late
         dep_delay >= 60, arr_delay - dep_delay > 30)  # Were delayed by at least an hour, but made up over 30 minutes in flight


2. Sort flights to find the flights with longest departure delays. Find the flights that left earliest in the morning.

κ°€μž₯ κΈ΄ 좜발 지연이 μžˆλŠ” ν•­κ³΅νŽΈ

flights %>%
  arrange(desc(dep_delay))

κ°€μž₯ 이λ₯Έ μ‹œκ°„μ— μΆœλ°œν•œ ν•­κ³΅νŽΈ

flights %>%
  arrange(dep_time) %>%
  filter(!is.na(dep_time))  

3. Sort flights to find the fastest flights (Hint: try sorting by a calculation).

λΉ„ν–‰ 속도λ₯Ό κΈ°μ€€μœΌλ‘œ μ •λ ¬ν•  수 μžˆλ‹€. μ†λ„λŠ” distance(거리)λ₯Ό air_time(λΉ„ν–‰ μ‹œκ°„)으둜 λ‚˜λˆ„μ–΄ 계산할 수 μžˆλ‹€.

flights %>%
  mutate(speed = distance / (air_time / 60)) %>%
  arrange(desc(speed))
NA

4. Which flights travelled the farthest? Which travelled the shortest?

κ°€μž₯ 멀리 μ΄λ™ν•œ ν•­κ³΅νŽΈ

flights %>%
  arrange(desc(distance))

κ°€μž₯ 짧게 μ΄λ™ν•œ ν•­κ³΅νŽΈ

flights %>%
  arrange(distance)

5. Does it matter what order you used filter() and arrange() in if you’re using both? Why/why not? Think about the results and how much work the functions would have to do.

filter()와 arrange()의 μˆœμ„œκ°€ μ€‘μš”ν•  수 μžˆλ‹€.
filter()둜 λ¨Όμ € 데이터λ₯Ό 쀄이면 arrange()κ°€ μ •λ ¬ν•΄μ•Ό ν•  데이터 양이 쀄어듀어 μž‘μ—… 속도가 빨라질 수 μžˆλ‹€.
arrange()λ₯Ό λ¨Όμ € μ‚¬μš©ν•˜λ©΄ 전체 데이터셋을 μ •λ ¬ν•œ ν›„ ν•„ν„°λ§ν•˜λ―€λ‘œ λΆˆν•„μš”ν•œ 계산이 λ°œμƒν•  수 μžˆλ‹€.

λ”°λΌμ„œ, 일반적으둜 filter()λ₯Ό λ¨Όμ € μ‚¬μš©ν•˜μ—¬ 데이터λ₯Ό 쀄인 ν›„ arrange()λ₯Ό μ‚¬μš©ν•˜λŠ” 것이 더 νš¨μœ¨μ μ΄λ‹€.

5.3.5

1. Currently dep_time and sched_dep_time are convenient to look at, but hard to compute with because they’re not really continuous numbers. Convert them to a more convenient representation of number of minutes since midnight.

flights <- flights %>%
  mutate(
    dep_time_mins = (dep_time %/% 100) * 60 + (dep_time %% 100),
    sched_dep_time_mins = (sched_dep_time %/% 100) * 60 + (sched_dep_time %% 100)
  )

flights
NA

2. Compare air_time with arr_time - dep_time. What do you expect to see? What do you see? What do you need to do to fix it?

air_time은 μ‹€μ œ λΉ„ν–‰ μ‹œκ°„(λΆ„ λ‹¨μœ„)을 λ‚˜νƒ€λ‚Έλ‹€.
반면, arr_time - dep_time은 좜발 μ‹œκ°„κ³Ό 도착 μ‹œκ°„μ„ λΉΌμ„œ 얻은 κ°’μ΄λ―€λ‘œ μ •ν™•ν•œ λΉ„ν–‰ μ‹œκ°„μ„ λ‚˜νƒ€λ‚΄μ§€ μ•Šμ„ 수 μžˆλ‹€.
λ”°λΌμ„œ arr_timeκ³Ό dep_time도 HHMM ν˜•μ‹μ΄λ―€λ‘œ, 이 값을 λΆ„μœΌλ‘œ λ³€ν™˜ν•΄μ•Ό ν•œλ‹€.

flights <- flights %>%
  mutate(
    dep_time_mins = (dep_time %/% 100) * 60 + (dep_time %% 100),
    arr_time_mins = (arr_time %/% 100) * 60 + (arr_time %% 100),
    air_time_calculated = arr_time_mins - dep_time_mins
  )

# 비ꡐ
flights %>%
  select(air_time, air_time_calculated)
NA

4. Brainstorm as many ways as possible to select dep_time, dep_delay, arr_time, and arr_delay from flights.

flights %>% select(dep_time, dep_delay, arr_time, arr_delay)
NA
flights %>% select(contains("dep"), contains("arr"))
NA
flights %>% select(starts_with("dep"), starts_with("arr"))
NA
flights %>% select(matches("^(dep|arr)_(time|delay)"))
NA
variables <- c("dep_time", "dep_delay", "arr_time", "arr_delay")
flights %>% select(all_of(variables))
NA
flights %>% select(4, 6, 7, 9)  # dep_time, dep_delay, arr_time, arr_delay
NA

5. What happens if you include the name of a variable multiple times in a select() call?

select() ν•¨μˆ˜μ—μ„œ λ™μΌν•œ λ³€μˆ˜ 이름을 μ—¬λŸ¬ 번 ν¬ν•¨ν•˜λ©΄, κ²°κ³Όμ—λŠ” ν•΄λ‹Ή λ³€μˆ˜κ°€ ν•œ 번만 μ„ νƒλœλ‹€.
dplyr은 select()μ—μ„œ μ€‘λ³΅λœ λ³€μˆ˜ 이름을 μžλ™μœΌλ‘œ μ œκ±°ν•˜μ—¬, 결과에 μ€‘λ³΅λ˜μ§€ μ•Šλ„λ‘ μ²˜λ¦¬ν•œλ‹€.

6. What does the any_of() function do? Why might it be helpful in conjunction with this vector?

variables <- c("year", "month", "day", "dep_delay", "arr_delay")
variables
[1] "year"      "month"    
[3] "day"       "dep_delay"
[5] "arr_delay"

any_of()λŠ” dplyr의 헬퍼 ν•¨μˆ˜λ‘œ, μ§€μ •λœ 칼럼 이름 λͺ©λ‘ 쀑 μ‹€μ œ 데이터셋에 μ‘΄μž¬ν•˜λŠ” 칼럼만 선택할 수 μžˆλ„λ‘ ν•΄μ€€λ‹€.

즉, μ§€μ •ν•œ 칼럼 이름 쀑 일뢀가 데이터셋에 없어도 였λ₯˜λ₯Ό λ°œμƒμ‹œν‚€μ§€ μ•Šκ³ , μ‹€μ œλ‘œ μ‘΄μž¬ν•˜λŠ” 칼럼만 μ„ νƒν•œλ‹€.

any_of()κ°€ μœ μš©ν•œ 이유:
  μ£Όμ–΄μ§„ 벑터가 μœ„μ˜ μ½”λ“œμ™€ 같을 λ•Œ,  select(flights, any_of(variables))와 같이 μ‚¬μš©ν•˜λ©΄ flights 데이터셋에 ν¬ν•¨λœ variables의 칼럼만 μ„ νƒλœλ‹€. 벑터에 μžˆλŠ” λͺ¨λ“  칼럼이 flights 데이터셋에 없어도 였λ₯˜κ°€ λ°œμƒν•˜μ§€ μ•ŠμœΌλ©°, 데이터셋에 μ‘΄μž¬ν•˜λŠ” 칼럼만 μ„ νƒλœλ‹€. 

즉, μ½”λ“œ μœ μ—°μ„±, μ½”λ“œ μž¬μ‚¬μš©μ„±.

7. Does the result of running the following code surprise you? How do the select helpers deal with case by default? How can you change that default?

select(flights, contains("TIME")) #λ‹€μ‹œ

contains() ν•¨μˆ˜κ°€ λŒ€μ†Œλ¬Έμž ꡬ뢄이 μ•ˆλœλ‹€.
λŒ€μ†Œλ¬Έμžλ₯Ό κ΅¬λΆ„ν•˜μ—¬ κ²€μƒ‰ν•˜λ €λ©΄ ignore.case = FALSE μ˜΅μ…˜μ„ μΆ”κ°€ν•  수 μžˆλ‹€.

select(flights, contains("TIME", ignore.case = FALSE)) 

5.4.6.

1. Which carrier has the worst delays? Challenge: can you disentangle the effects of bad airports vs.Β bad carriers? Why/why not? (Hint: think about flights %>% group_by(carrier, dest) %>% summarise(n()))

carrier_delays <- flights %>%
  group_by(carrier) %>%
  summarise(avg_dep_delay = mean(dep_delay, na.rm = TRUE)) %>%
  arrange(desc(avg_dep_delay))

carrier_delays

worst delays: F9

challenge:can you disentangle the effects of bad airports vs.Β bad carriers? Why/why not?

carrier_dest_counts <- flights %>%
  group_by(carrier, dest) %>%
  summarise(count = n(), avg_dep_delay = mean(dep_delay, na.rm = TRUE)) %>%
  arrange(desc(avg_dep_delay))
`summarise()` has grouped output by 'carrier'. You can override using the `.groups` argument.
carrier_dest_counts
NA

νŠΉμ • 항곡사-곡항 μ‘°ν•©μ—μ„œμ˜ 평균 μ§€μ—° μ‹œκ°„μ„ 확인할 수 μžˆλ‹€. ν•˜μ§€λ§Œ 이 λ°©μ‹μœΌλ‘œ 곡항과 항곡사 κ°„μ˜ μ§€μ—° 원인을 μ™„λ²½ν•˜κ²Œ κ΅¬λΆ„ν•˜λŠ” 것은 μ–΄λ ΅λ‹€. 예λ₯Ό λ“€μ–΄, 항곡기 μŠ€μΌ€μ€„μ΄λ‚˜ νŠΉμ • μ‹œκ°„λŒ€μ˜ ν˜Όμž‘λ„, 날씨 λ“±μ˜ λ‹€λ₯Έ μš”μ†Œλ“€λ„ 지연에 영ν–₯을 미치기 λ•Œλ¬Έμ—, λ‹¨μˆœνžˆ 곡항과 ν•­κ³΅μ‚¬λ§Œμ„ λΉ„κ΅ν•΄μ„œλŠ” 전체적인 μ§€μ—° 원인을 λͺ…ν™•ν•˜κ²Œ κ΅¬λΆ„ν•˜κΈ° μ–΄λ ΅λ‹€. μ§€μ—° 원인을 μ™„μ „νžˆ μ΄ν•΄ν•˜λ €λ©΄ 더 λ§Žμ€ λ³€μˆ˜λ₯Ό κ³ λ €ν•΄μ•Ό ν•œλ‹€.

2 . What does the sort argument to count() do. Can you explain it in terms of the dplyr verbs you’ve learned so far?

count() ν•¨μˆ˜μ˜ sort = TRUE 인수λ₯Ό μ‚¬μš©ν•˜λ©΄, κ²°κ³Όκ°€ μžλ™μœΌλ‘œ μΉ΄μš΄νŠΈκ°€ 큰 μˆœμ„œλŒ€λ‘œ μ •λ ¬λœλ‹€.

carrier_counts <- flights %>%
  count(carrier, sort = TRUE)
carrier_counts

sort = TRUEλŠ” arrange(desc(n))와 같은 κΈ°λŠ₯을 μˆ˜ν–‰ν•œλ‹€κ³  말할 수 μžˆλ‹€.

carrier_counts <- flights %>%
  count(carrier) %>%         
  arrange(desc(n)) #λ‚΄λ¦Όμ°¨μˆœ μ •λ ¬

carrier_counts

9.3.3

1. ν•„λ“œκ°€ β€œ|” 둜 λΆ„λ¦¬λœ νŒŒμΌμ„ 읽으렀면 μ–΄λ–€ ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜κ² λŠ”κ°€?

read_delim()

read_delim("data/students.csv", delim = "|", show_col_types = FALSE)

2. read_csv() 와 read_tsv() κ°€ κ³΅ν†΅μœΌλ‘œ κ°€μ§„ μΈμˆ˜λŠ” file, skip, comment 외에 또 무엇이 μžˆλŠ”κ°€?

intersect(names(formals(read_csv)), names(formals(read_tsv)))
 [1] "file"            "col_names"       "col_types"       "col_select"      "id"              "locale"          "na"             
 [8] "quoted_na"       "quote"           "comment"         "trim_ws"         "skip"            "n_max"           "guess_max"      
[15] "name_repair"     "num_threads"     "progress"        "show_col_types"  "skip_empty_rows" "lazy"           

col_names와 col_types: μ—΄ 이름과 열을 μ–΄λ–»κ²Œ ꡬ문 뢄석할지λ₯Ό μ§€μ •

locale: 인코딩 μ„€μ •κ³Ό μ†Œμˆ˜μ  ꡬ뢄 기호(β€œ.” λ˜λŠ” β€œ,”) 등을 κ²°μ •ν•˜λŠ” 데 μ€‘μš”

na와 quoted_na: κ²°μΈ‘κ°’μœΌλ‘œ μ²˜λ¦¬ν•  λ¬Έμžμ—΄μ„ μ œμ–΄

trim_ws: μ…€ μ•žλ’€μ˜ 곡백을 μ œκ±°ν•œ ν›„ ꡬ문 뢄석

n_max: 읽을 μ΅œλŒ€ ν–‰ 수λ₯Ό μ„€μ •

guess_max: μ—΄ μœ ν˜•μ„ μΆ”μΈ‘ν•  λ•Œ μ‚¬μš©ν•  ν–‰ 수λ₯Ό μ„€μ •

progress: μ§„ν–‰ ν‘œμ‹œμ€„μ„ ν‘œμ‹œν• μ§€ μ—¬λΆ€λ₯Ό κ²°μ •


3. read_fwf() μ—μ„œ κ°€μž₯ μ€‘μš”ν•œ μΈμˆ˜λŠ” 무엇인가?

read_fwf() ν•¨μˆ˜μ—μ„œ κ°€μž₯ μ€‘μš”ν•œ μΈμˆ˜λŠ” col_positions이닀.
이 μΈμˆ˜λŠ” 데이터 열이 μ‹œμž‘ν•˜κ³  λλ‚˜λŠ” μœ„μΉ˜λ₯Ό ν•¨μˆ˜μ— μ•Œλ €μ£Όλ©°, β€œfixed-width formatsβ€μ˜ νŒŒμΌμ„ 읽을 λ•Œ μ‚¬μš©λœλ‹€.

4. CSV 파일의 λ¬Έμžμ—΄μ— μ‰Όν‘œκ°€ ν¬ν•¨λ˜λŠ” κ²½μš°κ°€ μžˆλ‹€. 그것듀이 문제λ₯Ό 일으 ν‚€μ§€ μ•Šκ²Œ ν•˜λ €λ©΄ ” ν˜Ήμ€ ’와 같은 인용 문자둜 λ‘˜λŸ¬μ‹ΈμΌ ν•„μš”κ°€ μžˆλ‹€. read_csv() λŠ” 인용 λ¬Έμžκ°€ β€œλΌκ³  κ°€μ •ν•œλ‹€. 이λ₯Ό λ³€κ²½ν•˜λ €λ©΄ read_delim() 을 λŒ€μ‹  μ‚¬μš©ν•˜λ©΄ λœλ‹€. λ‹€μŒ ν…μŠ€νŠΈλ₯Ό λ°μ΄ν„°ν”„λ ˆμž„μœΌλ‘œ 읽으렀면 μ–΄λ–€ 인수λ₯Ό μ„€μ •ν•΄μ•Όν•˜λŠ”κ°€?

"x,y\n1,'a,b'"
[1] "x,y\n1,'a,b'"

read_delim()을 μ‚¬μš©ν•  경우, ꡬ뢄 문자둜 β€œ,”λ₯Ό μ§€μ •ν•˜κ³  quote 인수λ₯Ό μ„€μ •ν•΄μ•Ό ν•œλ‹€.

x <- "x,y\n1,'a,b'"
read_delim(x, ",", quote = "'", show_col_types = FALSE)

λ˜ν•œ read_csv()λŠ” quote 인수λ₯Ό μ§€μ›ν•œλ‹€.

read_csv(x, quote = "'", show_col_types = FALSE)

5. λ‹€μŒ 각 인라인 CSV νŒŒμΌμ— μ–΄λ–€ λ¬Έμ œκ°€ μžˆλŠ”μ§€ ν™•μΈν•˜λΌ. μ½”λ“œλ₯Ό μ‹€ν–‰ν•˜λ©΄ μ–΄λ–»κ²Œ λ˜λŠ”κ°€?

#1
read_csv("a,b\n1,2,3\n4,5,6")
Warning: One or more parsing issues, call `problems()` on your data frame for details, e.g.:
  dat <- vroom(...)
  problems(dat)Rows: 2 Columns: 2── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
dbl (1): a
num (1): b
β„Ή Use `spec()` to retrieve the full column specification for this data.
β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> Warning: One or more parsing issues, see `problems()` for details
#> Rows: 2 Columns: 2
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: ","
#> dbl (1): a
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 2 Γ— 2
#>       a     b
#>   <dbl> <dbl>
#> 1     1    23
#> 2     4    56

두 개의 μ—΄(a와 b)만 μ§€μ •λ˜μ–΄ μžˆμ§€λ§Œ, 각 ν–‰μ—λŠ” μ„Έ 개의 값이 μžˆμŠ΅λ‹ˆλ‹€. κ·Έλž˜μ„œ λ§ˆμ§€λ§‰ μ—΄ 값이 μž˜λ¦°λ‹€.

#2
read_csv("a,b,c\n1,2\n1,2,3,4")
Warning: One or more parsing issues, call `problems()` on your data frame for details, e.g.:
  dat <- vroom(...)
  problems(dat)Rows: 2 Columns: 3── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
dbl (2): a, b
num (1): c
β„Ή Use `spec()` to retrieve the full column specification for this data.
β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> Warning: One or more parsing issues, see `problems()` for details
#> Rows: 2 Columns: 3
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: ","
#> dbl (2): a, b
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 2 Γ— 3
#>       a     b     c
#>   <dbl> <dbl> <dbl>
#> 1     1     2    NA
#> 2     1     2    34

ν—€λ”μ—λŠ” μ„Έ 개의 열이 μžˆμ§€λ§Œ, 첫 번째 ν–‰μ—λŠ” 두 개의 κ°’λ§Œ 있고 두 번째 ν–‰μ—λŠ” λ„€ 개의 값이 μžˆλ‹€. 첫 번째 ν–‰μ—μ„œλŠ” c 열이 결츑치둜 μ„€μ •λ˜κ³ , 두 번째 ν–‰μ—μ„œλŠ” μΆ”κ°€λœ 값이 μž˜λ¦°λ‹€.

#3
read_csv("a,b\n\"1")
Rows: 0 Columns: 2── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (2): a, b
β„Ή Use `spec()` to retrieve the full column specification for this data.
β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> Rows: 0 Columns: 2
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: ","
#> chr (2): a, b
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 0 Γ— 2
#> # … with 2 variables: a <chr>, b <chr>

1μ΄λΌλŠ” μ—΄ 값이 μ—΄λ¦Ό ν‘œμ‹œλ§Œ 있고 λ‹«νžˆμ§€ μ•Šμ•˜κΈ° λ•Œλ¬Έμ— μ˜λ„ν•œ λ°”κ°€ λΆˆλΆ„λͺ…ν•˜λ‹€. 인용이 λ‹«νžˆμ§€ μ•Šμ•„ aλŠ” μˆ«μžν˜•μœΌλ‘œ 처리되고, bλŠ” 결츑치둜 μ„€μ •λœλ‹€.

#4
read_csv("a,b\n1,2\na,b")
Rows: 2 Columns: 2── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (2): a, b
β„Ή Use `spec()` to retrieve the full column specification for this data.
β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> Rows: 2 Columns: 2
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: ","
#> chr (2): a, b
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 2 Γ— 2
#>   a     b    
#>   <chr> <chr>
#> 1 1     2    
#> 2 a     b

β€œa”와 β€œb”가 μˆ«μžκ°€ μ•„λ‹Œ λ¬Έμžμ—΄μ„ ν¬ν•¨ν•˜κ³  μžˆμœΌλ―€λ‘œ λ¬Έμžν˜•μœΌλ‘œ μ²˜λ¦¬λœλ‹€. 이것이 μ˜λ„λœ 것일 μˆ˜λ„ 있고, 1,2와 a,bκ°€ κ°’μœΌλ‘œ μ§€μ •λ˜κΈΈ μ›ν–ˆλ˜ 것일 μˆ˜λ„ μžˆλ‹€.

#5
read_csv("a;b\n1;3")
Rows: 1 Columns: 1── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): a;b
β„Ή Use `spec()` to retrieve the full column specification for this data.
β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> Rows: 1 Columns: 1
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: ","
#> chr (1): a;b
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 1 Γ— 1
#>   `a;b`
#>   <chr>
#> 1 1;3

데이터가 β€œ;β€λ‘œ κ΅¬λΆ„λ˜μ–΄ μžˆλ‹€. 이 경우 read_csv2()λ₯Ό μ‚¬μš©ν•˜λ©΄ 더 μ ν•©ν•˜λ‹€.

LS0tCnRpdGxlOiAiSFc3IgphdXRob3I6ICIyMDIyNTU2Njkg7J207KeE7IaUIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgojIyAzLjIuNAoKIyMjIyAxLiBnZ3Bsb3QoZGF0YSA9IG1wZykg7J2EIOyLpO2Wie2VmOudvC4g7Ja065akIOqyg+ydtCDrgpjsmKTripTqsIA/CmBgYHtyfQpnZ3Bsb3QoZGF0YT1tcGcpCmBgYApnZ3Bsb3Qg7ZWo7IiY66eM7Jy866Gc64qUIOq3uOuemO2UhOulvCDqt7jrprQg7IiYIOyXhuq4sCDrlYzrrLjsl5Ag7JWE66y06rKD64+EIOuCmOyYpOyngCDslYrripTri6QuICAKZ2dwbG907J2AIOq3uOuemO2UhOulvCDqt7jrpqzquLAg7JyE7ZWcIOq4sOuzuCDti4DsnYQg7IOd7ISx7ZWgIOu/kCwg642w7J207YSw66W8IOyWtOuWpCDrsKnsi53snLzroZwg7Iuc6rCB7ZmU7ZWg7KeA7JeQIOuMgO2VnCDstpTqsIDsoIHsnbgg7KCV67O0KOyYiDogYWVzLCBnZW9tX3BvaW50LCBnZW9tX2xpbmUg65OxKeqwgCDtlYTsmpTtlZjri6QuICAKPGJyPgoKIyMjIyAyLm1wZyDsl5Ag7ZaJ7J20IOuqh+qwnCDsnojripTqsIA/IOyXtOydgCDrqofqsJzsnbjqsIA/IApgYGB7cn0KZGltKG1wZykKYGBgCipkaW0oKSrsnYQg7IKs7Jqp7ZWY66m0IG1wZyDrjbDsnbTthLDshYvsnZgg7ZaJKHJvdynqs7wg7Je0KGNvbHVtbikg6rCc7IiY66W8IO2ZleyduO2VoCDsiJgg7J6I64ukLgoK7ZaJOiBtcGcg642w7J207YSw7IWL7JeQ64qUIOy0nSAyMzTqsJzsnZgg7ZaJ7J20IOyeiOuLpC4gIArsl7Q6IG1wZyDrjbDsnbTthLDshYvsl5DripQg7LSdIDEx6rCc7J2YIOyXtOydtCDsnojri6QuICAKPGJyPgoKIyMjIyAzLiBkcnYg67OA7IiY64qUIOustOyXh+ydhCDsnZjrr7jtlZjripTqsIA/IOydtOulvCDslYzslYTrs7TquLAg7JyE7ZW0ID9tcGcg7J2EIOyLpO2Wie2VmOyXrCDrj4Tsm4Drp5DsnYQg7J297Jy86528LgpgYGB7cn0KP21wZwpgYGAKZHJ2ICAKdGhlIHR5cGUgb2YgZHJpdmUgdHJhaW4sIHdoZXJlIGYgPSBmcm9udC13aGVlbCBkcml2ZSwgciA9IHJlYXIgd2hlZWwgZHJpdmUsIDQgPSA0d2QgIAo8YnI+CmRydiDrs4DsiJjripQg7J6Q64+Z7LCo7J2YIOq1rOuPmSDrsKnsi53snYQg64KY7YOA64K464ukLiAgCmbripQg7KCE66Wc6rWs64+ZKGZyb250LXdoZWVsIGRyaXZlKSwgIApy64qUIO2bhOulnOq1rOuPmShyZWFyLXdoZWVsIGRyaXZlKSwgIAo064qUIOyCrOulnOq1rOuPmShmb3VyLXdoZWVsIGRyaXZlKeydhCDsnZjrr7jtlZzri6QuICAKPGJyPgoKIyMjIyA0LiBod3kgdnMgY3lsIOyCsOygkOuPhOulvCDqt7jrpqzrnbwuCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZywgYWVzKHggPSBod3ksIHkgPSBjeWwpKSArIGdlb21fcG9pbnQoKQpgYGAKCjxicj4KCiMjIyMgNS4gY2xhc3MgdnMgZHJ2IOyCsOygkOuPhOulvCDqt7jrpqzrqbQg7Ja065a76rKMIOuQmOuKlOqwgD8g7J20IO2UjOuhr+ydgCDsmZwg7JO466qo6rCAIOyXhuuKlOqwgD8KYGBge3J9CmdncGxvdChkYXRhID0gbXBnLCBhZXMoeCA9IGNsYXNzLCB5ID0gZHJ2KSkgKyBnZW9tX3BvaW50KCkKYGBgCgpjbGFzc+yZgCBkcnYg67OA7IiY64qUIOywqOufieyiheulmOyZgCDqtazrj5nrsKnsi53snbTrnbzripQg7Ji17IWYIOygleuztOuTpCDsoJXrj4Trp4wg64KY7YOA64K064qUIOyIq+yekOqwgCDslYTri4wg67KU7KO87ZiVIOuNsOydtO2EsOydtOuLpC4gIArrlLDrnbzshJwg7LaU7IS464KYIOq0gOqzhOulvCDtjIzslYXtlZjripTrjbDsl5DripQg7IKw7KCQ64+E66W8IOydtOyaqe2VnCBwbG907J20IOygge2Vqe2VmOyngCDslYrri6Tqs6Ag67O07J2464ukLiAKCiMjIDMuMy4xCgojIyMjIDEuIFdoYXTigJlzIGdvbmUgd3Jvbmcgd2l0aCB0aGlzIGNvZGU/IFdoeSBhcmUgdGhlIHBvaW50cyBub3QgYmx1ZT8gIApgYGB7cn0KZ2dwbG90KGRhdGEgPSBtcGcpICsgCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSwgY29sb3IgPSAiYmx1ZSIpKQpgYGAK7L2U65Oc7JeQ7IScICJibHVlIuqwgCDrspTso7ztmJUg6rCS7Jy866GcIOyduOyLneuQmOyWtCDsg4nsg4HsnLzroZwg7KCB7Jqp65CY7KeAIOyViuydgCDqsoPsnbTsnbTri6QuICAgCiJibHVlIuulvCDrs4DsiJjroZwg6rCE7KO87ZWY7KeAIOyViuqzoCDqs6DsoJXrkJwg7IOJ7IOB7Jy866GcIOyEpOygle2VmOugpOuptCwgYWVzKCkg7ZWo7IiYIOuwluyXkCBjb2xvciA9ICJibHVlIuulvCDsp4DsoJXtlbTslbwg7ZWc64ukLiAgCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBtcGcpICsgCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSksIGNvbG9yID0gImJsdWUiKQpgYGAK7J2066CH6rKMIO2VmOuptCDrqqjrk6Ag7KCQ7J20IO2MjOuegOyDieycvOuhnCDtkZzsi5zrkJjrqbAsIOuylOyjvO2YlSDqsJLsnLzroZwg7J6Y66q7IOunpO2VkeuQmOyngCDslYrripTri6QuICAgIAo8YnI+CgojIyMjIDIuIFdoaWNoIHZhcmlhYmxlcyBpbiBtcGcgYXJlIGNhdGVnb3JpY2FsPyBXaGljaCB2YXJpYWJsZXMgYXJlIGNvbnRpbnVvdXM/IChIaW50OiB0eXBlID9tcGcgdG8gcmVhZCB0aGUgZG9jdW1lbnRhdGlvbiBmb3IgdGhlIGRhdGFzZXQpLiBIb3cgY2FuIHlvdSBzZWUgdGhpcyBpbmZvcm1hdGlvbiB3aGVuIHlvdSBydW4gbXBnPyAgCmBgYHtyfQo/bXBnCnN0cihtcGcpCmBgYArrspTso7ztmJUg67OA7IiYOiBtYW51ZmFjdHVyZXIsIG1vZGVsLCB0cmFucywgZHJ2LCBmbCwgY2xhc3MgIArsl7Dsho3tmJUg67OA7IiYOiBkaXNwbCwgeWVhciwgY3lsLCBjdHksIGh3eSAgCjxicj4KKj9tcGcq66W8IOyLpO2Wie2VtCDqsIEg67OA7IiY7J2YIOyEpOuqheydhCDrs7wg7IiYIOyeiOqzoCwgKnN0cihtcGcpKuulvCDsi6TtlontlbQg6rCBIOuzgOyImOyXkCDrjIDtlZwg7Jyg7ZiV7J2EIO2ZleyduO2VtOuzvCDsiJgg7J6I64ukLiAgCuydtCDrlYwgZmFjdG9y66GcIO2RnOyLnOuQnCDrs4DsiJjripQg67KU7KO87ZiVLCBudW3snbTrgpggaW5066GcIO2RnOyLnOuQnCDrs4DsiJjripQg7Jew7IaN7ZiV7J2064ukLiAgCjxicj4KCiMjIyMgMy4gTWFwIGEgY29udGludW91cyB2YXJpYWJsZSB0byBjb2xvciwgc2l6ZSwgYW5kIHNoYXBlLiBIb3cgZG8gdGhlc2UgYWVzdGhldGljcyBiZWhhdmUgZGlmZmVyZW50bHkgZm9yIGNhdGVnb3JpY2FsIHZzLiBjb250aW51b3VzIHZhcmlhYmxlcz8gIApgYGB7cn0KZ2dwbG90KGRhdGEgPSBtcGcpICsKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5LCBjb2xvciA9IGh3eSwgc2l6ZSA9IGh3eSkpCgpgYGAK7Jew7IaN7ZiVIOuzgOyImOulvCAqKuyDieyDgShjb2xvcikqKuydtOuCmCAqKu2BrOq4sChzaXplKSoq7JeQIOunpO2Vke2VmOuptCDsg4nsg4HsnYAg6re46528642w7J207IWY7Jy866GcLCDtgazquLDripQg6rCS7JeQIOuUsOudvCDruYTroYDsoIHsnLzroZwg7Luk7KeA6rGw64KYIOyekeyVhOynhOuLpC4gIAo8YnI+CmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZywgYWVzKHggPSBkaXNwbCwgeSA9IGh3eSwgY29sb3IgPSBod3ksIHNpemUgPSBod3ksIHNoYXBlID0gaHd5KSkgKyAKICBnZW9tX3BvaW50KCkKCmBgYAoK7ZWY7KeA66eMIOyXsOyGje2YlSDrs4DsiJjrpbwgKirrqqjslpEoc2hhcGUpKirsl5Ag66ek7ZWR7ZWY66m0IOychOyZgCDqsJnsnbQg7Jik66WY6rCAIOuwnOyDne2VnOuLpC4gICAgCuuqqOyWkeydgCDrs7TthrUg67KU7KO87ZiVIOuNsOydtO2EsOulvCDsnITtlbQg7ISk6rOE65CY7Ja0LCDqsJLsl5Ag65Sw6528IOqzoOycoO2VnCDrqqjslpHsnYQg7KeA7KCV7ZWgIOyImCDsl4bquLAg65WM66y47J2064ukLiAgCjxicj4KYGBge3J9CmdncGxvdChkYXRhID0gbXBnLCBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5LCBjb2xvciA9IGNsYXNzLCBzaXplID0gY2xhc3MsIHNoYXBlID0gY2xhc3MpKSArIAogIGdlb21fcG9pbnQoKQpgYGAK7JyE7JmAIOqwmeydtCDrspTso7ztmJUg67OA7IiY64qUIGNvbG9y7JmAIHNoYXBl7JeQIOunpO2VkeydtCDsoIHtlantlZjrqbAsIOyDieyDgeydtOuCmCDrqqjslpHsnYQg67KU7KO867OE66GcIOuLpOultOqyjCDsp4DsoJXtlaAg7IiYIOyeiOuLpC4gIApzaXpl7JeQIOunpO2Vke2VmOuptCDsnZjrr7jqsIAg66qo7Zi47ZW07KeIIOyImCDsnojri6QuICAKPGJyPgoKIyMjIyA0LiBXaGF0IGhhcHBlbnMgaWYgeW91IG1hcCB0aGUgc2FtZSB2YXJpYWJsZSB0byBtdWx0aXBsZSBhZXN0aGV0aWNzPyAgCuqwmeydgCDrs4DsiJjrpbwg7Jes65+sIOyGjeyEsSjsmIg6IOyDieyDgeqzvCDtgazquLAp7JeQIOunpO2Vke2VmOuptCDtlbTri7kg67OA7IiY7J2YIOuzgO2ZlOyXkCDrlLDrnbwg7Jes65+sIOyLnOqwgeyggSDsmpTshozqsIAg64+Z7Iuc7JeQIOuLrOudvOynhOuLpC4gIApgYGB7cn0KZ2dwbG90KGRhdGEgPSBtcGcpICsKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5LCBjb2xvciA9IGh3eSwgc2l6ZSA9IGh3eSkpCmBgYArsnITsnZgg6rK97JqwIGh3eeyXkCDrlLDrnbwg7IOJ7IOB6rO8IO2BrOq4sOqwgCDrj5nsi5zsl5Ag67CU64CQ64ukLiAgCuydtCDrsKnrspXsnYAg67OA7IiY6rCAIOqwleyhsOuQmOyngOunjCDqsIDrj4XshLHsnbQg65ao7Ja07KeIIOyImCDsnojri6QuICAgIAo8YnI+CgojIyMjIDUuIFdoYXQgZG9lcyB0aGUgc3Ryb2tlIGFlc3RoZXRpYyBkbz8gV2hhdCBzaGFwZXMgZG9lcyBpdCB3b3JrIHdpdGg/IChIaW50OiB1c2UgP2dlb21fcG9pbnQpICAKYGBge3J9Cj9nZW9tX3BvaW50CgpnZ3Bsb3QoZGF0YSA9IG1wZykgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpLCBzaGFwZSA9IDIxLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiYmx1ZSIsIHN0cm9rZSA9IDIpCmBgYAoqKnN0cm9rZSoqIOyGjeyEseydgCBnZ3Bsb3Qy7JeQ7IScIOyZuOqzveyEoCDrkZDqu5jrpbwg7KGw7KCV7ZWY64qUIOyXre2VoOydhCDtlZzri6QuICAgIArtirntnogsICoq7LGE7JuA7IOJ6rO8IOyZuOqzveyEoCDsg4nsnYQg6rCB6rCBIOyEpOygle2VoCDsiJgg7J6I64qUIOuPhO2YlSgyMeuyiH4yNOuyiCkqKuyXkOunjCDsoIHsmqnrkJzri6QuICAgIAo8YnI+CgojIyMjIDYuIFdoYXQgaGFwcGVucyBpZiB5b3UgbWFwIGFuIGFlc3RoZXRpYyB0byBzb21ldGhpbmcgb3RoZXIgdGhhbiBhIHZhcmlhYmxlIG5hbWUsIGxpa2UgYWVzKGNvbG91ciA9IGRpc3BsIDwgNSk/IE5vdGUsIHlvdeKAmWxsIGFsc28gbmVlZCB0byBzcGVjaWZ5IHggYW5kIHkuIApgYGB7cn0KZ2dwbG90KGRhdGEgPSBtcGcpICsKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5LCBjb2xvciA9IGRpc3BsIDwgNSkpCmBgYApkaXNwbCA8IDXsspjrn7wg7KGw6rG066y47J2EIOyCrOyaqe2VtCDsg4nsg4HsnYQg66ek7ZWR7ZWY66m0LCBUUlVFL0ZBTFNFIOuFvOumrOqwkuycvOuhnCDtkZzsi5zrkJzri6QuICAKPGJyPgoKIyMgMy41LjEKCiMjIyMgMS4gV2hhdCBoYXBwZW5zIGlmIHlvdSBmYWNldCBvbiBhIGNvbnRpbnVvdXMgdmFyaWFibGU/CuqwgSDqs6DsnKAg6rCS7JeQIOuMgO2VtCDrs4Trj4TsnZgg7Yyo64SQ7J20IOunjOuTpOyWtOynhOuLpC4g7Jew7IaN7ZiVIOuzgOyImOyXkCDqs6DsnKAg6rCS7J20IOunjuydhCDqsr3smrAg7Yyo64SQ7J20IOuEiOustCDrp47slYTsoLgsIOq3uOuemO2UhOqwgCDrs7XsnqHtlZjqs6Ag7ZW07ISd7ZWY6riwIOyWtOugpOybjOyniCDsiJgg7J6I64ukLiAqKmZhY2V0KirsnYAg7J2867CY7KCB7Jy866GcIOuylOyjvO2YlSDrs4DsiJjsmYAg7ZWo6ruYIOyCrOyaqe2VoCDrlYwg6rCA7J6lIOycoOyaqe2VmOuLpC4gIAoKIyMjIyAyLiBXaGF0IGRvIHRoZSBlbXB0eSBjZWxscyBpbiBwbG90IHdpdGggZmFjZXRfZ3JpZChkcnYgfiBjeWwpIG1lYW4/IEhvdyBkbyB0aGV5IHJlbGF0ZSB0byB0aGlzIHBsb3Q/CmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZykgKyAKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGRydiwgeSA9IGN5bCkpCmBgYArruYgg7IWA7J2AIGRyduyZgCBjeWzsnZgg7Yq57KCVIOyhsO2VqeydtCDrjbDsnbTthLDsl5Ag7KG07J6sIO2VmOyngCDslYrripTri6TripQg6rKD7J2EIOydmOuvuO2VnOuLpC4gICAK7JyE7J2YIOqyveyasCDsmIjrpbwg65Ok7Ja0LCBkcnY9NOydvCDrlYwgY3lsPTXsnbgg7KGw7ZWp7J2AIOyXhuuLpC4gICAgCjxicj4KCiMjIyMgMy4gV2hhdCBwbG90cyBkb2VzIHRoZSBmb2xsb3dpbmcgY29kZSBtYWtlPyBXaGF0IGRvZXMgLiBkbz8KYGBge3J9CmdncGxvdChkYXRhID0gbXBnKSArIAogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKSArCiAgZmFjZXRfZ3JpZChkcnYgfiAuKQpgYGAK7JyE7J2YIOqyveyasCwgZHJ2IOuzgOyImOulvCDtlokocm93KSDrsKntlqXsnLzroZwgZmVjZXTtlZzri6QuICAK6rCBIGRydiDqsJLrp4jri6Qg7ZaJ7J20IOunjOuTpOyWtOyngOqzoCwgZGlzcGzqs7wgaHd5IOuzgOyImOydmCDsgrDsoJDrj4TqsIAg6rCBIGZhY2V07JeQIOuCmO2DgOuCnOuLpC4gIArsl6zquLDshJwgLuuKlCDtlbTri7kg67Cp7ZalKOyXtCnsnYQg67mE7JuM65GU64uk64qUIOydmOuvuOuhnCBmYWNldOydhCAqKu2WiSDrsKntlqUqKuycvOuhnOunjCDrgpjriIwg65WMIOyCrOyaqe2VnOuLpC4gIAo8YnI+CgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBtcGcpICsgCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpICsKICBmYWNldF9ncmlkKC4gfiBjeWwpCmBgYArsnITsnZgg6rK97JqwLCBjeWwg67OA7IiY66W8IOyXtChjb2x1bW4pIOuwqe2WpeycvOuhnCBmYWNldO2VmOyXrCDqsIEgY3lsIOqwkuuniOuLpCDsl7TsnbQg66eM65Ok7Ja0IOynhOuLpC4gIArsl6zquLDshJwgLuuKlCDtlbTri7kg67Cp7ZalKO2WiSnsnYQg67mE7JuM65GU64uk64qUIOydmOuvuOuhnCBmYWNldOydhCAqKuyXtCDrsKntlqUqKuycvOuhnOunjCDrgpjriIwg65WMIOyCrOyaqe2VnOuLpC4gIAo8YnI+CgojIyMjIDQuIFRha2UgdGhlIGZpcnN0IGZhY2V0ZWQgcGxvdCBpbiB0aGlzIHNlY3Rpb246CmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZykgKyAKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkgKyAKICBmYWNldF93cmFwKH4gY2xhc3MsIG5yb3cgPSAyKQpgYGAKCiMjIyMgV2hhdCBhcmUgdGhlIGFkdmFudGFnZXMgdG8gdXNpbmcgZmFjZXRpbmcgaW5zdGVhZCBvZiB0aGUgY29sb3VyIGFlc3RoZXRpYz8gV2hhdCBhcmUgdGhlIGRpc2FkdmFudGFnZXM/IEhvdyBtaWdodCB0aGUgYmFsYW5jZSBjaGFuZ2UgaWYgeW91IGhhZCBhIGxhcmdlciBkYXRhc2V0PyAgCjxicj4KICAKICAKICDsnqXsoJA6ICAKICAKICAgIGZhY2V0X3dyYXDsnYAg7Yyo64SQ7J2EIOyXrOufrCDtlonqs7wg7Je07JeQIOyekOuPmeycvOuhnCDrsLDsuZjtlZjsl6wsIO2KueyglSDrs4DsiJjsnZgg6rCBIOqwkuyXkCDrjIDtlbQg7Yyo64SQ7J2EIOq1rOyEse2VoCDsiJgg7J6I7Ja0IOyLnOqwgeyggeycvOuhnCDquZTrgZTtlZjri6QuICAKICAgIAogICAg7IOJ7IOBKGNvbG9yKeydhCDsgqzsmqntlZjsl6wg67OA7IiY67OE66GcIOq1rOu2hO2VmOuKlCDrjIDsi6AsIGZhY2V07J2EIOyCrOyaqe2VmOuptCDrjZQg66qF7ZmV7ZWY6rKMIOu5hOq1kO2VoCDsiJgg7J6I64ukLiAKICAKICAgICAKICDri6jsoJA6ICAKICAKICAgIGZhY2V07J2YIOqwnOyImOqwgCDrp47slYTsp4gg6rK97JqwIOyghOyytCBwbG907J20IOuzteyeoe2VtOyniCDsiJgg7J6I6rOgLCDtirntnogg642w7J207YSw6rCAIO2BsCDqsr3smrAg7ZmU66m0IOqzteqwhOydhCDrp47snbQg7LCo7KeA7ZWgIOyImCDsnojri6QuICAKICAgIAogICAg7Yyo64SQ66GcIOuCmOuJnCDqsr3smrAsIOyghOyytOyggeyduCDrjbDsnbTthLAg67aE7Y+s66W8IOuPmeyLnOyXkCDruYTqtZDtlZjquLAg7Ja066Ck7Jq4IOyImCDsnojri6QuICAKICAKICAgICAgCiAg642w7J207YSwIOyFi+ydtCDrjZQg7Luk7KeIIOqyveyasDogIAogIAogICAg642w7J207YSw6rCAIOuNlCDsu6Tsp4Tri6TrqbQg7IOJ7IOBIOq1rOu2hOydtCDrjZQg7Zqo6rO87KCB7J28IOyImCDsnojri6QuIGZhY2V07J2AIOyggeuLue2VnCDrspTso7zsnZgg642w7J207YSwIOyFi+yXkOyEnCDrjZQg7Jyg7Jqp7ZWgIOqyg+ydtOuLpC4gIAogICAgCiAgICAKCiAgCgojIyMjIDUuIFJlYWQgP2ZhY2V0X3dyYXAuIFdoYXQgZG9lcyBucm93IGRvPyBXaGF0IGRvZXMgbmNvbCBkbz8gV2hhdCBvdGhlciBvcHRpb25zIGNvbnRyb2wgdGhlIGxheW91dCBvZiB0aGUgaW5kaXZpZHVhbCBwYW5lbHM/IFdoeSBkb2VzbuKAmXQgZmFjZXRfZ3JpZCgpIGhhdmUgbnJvdyBhbmQgbmNvbCBhcmd1bWVudHM/CmBgYHtyfQo/ZmFjZXRfd3JhcApgYGAKZmFjZXRfd3JhcOyXkOyEnCBucm9364qUIO2WiShyb3cp7J2YIOqwnOyImOulvCDsp4DsoJXtlZjqs6AsIG5jb2zsnYAg7Je0KGNvbHVtbinsnZgg6rCc7IiY66W8IOyngOygle2VnOuLpC4g7J20IOyYteyFmOydhCDthrXtlbQg7Yyo64SQ7J2YIOuwsOy5mOulvCDsobDsoJXtlaAg7IiYIOyeiOuLpC4gICAK7LaU6rCA66GcIHNjYWxlcyDsmLXshZjsnYQg7IKs7Jqp7ZWY7JesIOqwgSDtjKjrhJDsnZgg7LaVIOuylOychOulvCDsobDsoJXtlaAg7IiYIOyeiOuLpC4gIAoKPGJyPgpmYWNldF9ncmlk64qUIO2WieqzvCDsl7TroZwg6rOg7KCV65CcIOqyqeyekCDtmJXtg5zroZwgZmFjZXTsnYQg66eM65Ok6riwIOuVjOusuOyXkCBucm937JmAIG5jb2wg7Ji17IWY7J20IOyXhuuLpC4gZmFjZXRfZ3JpZOuKlCDsnbzrsJjsoIHsnLzroZwg65GQIOqwnOydmCDrspTso7ztmJUg67OA7IiYIOqwhCDqtIDqs4Trpbwg7Iuc6rCB7ZmU7ZWgIOuVjCDsgqzsmqntlZzri6QuICAKCgo8YnI+CgojIyMjIDYuIFdoaWNoIG9mIHRoZSBmb2xsb3dpbmcgdHdvIHBsb3RzIG1ha2VzIGl0IGVhc2llciB0byBjb21wYXJlIGVuZ2luZSBzaXplIChkaXNwbCkgYWNyb3NzIGNhcnMgd2l0aCBkaWZmZXJlbnQgZHJpdmUgdHJhaW5zPyBXaGF0IGRvZXMgdGhpcyBzYXkgYWJvdXQgd2hlbiB0byBwbGFjZSBhIGZhY2V0aW5nIHZhcmlhYmxlIGFjcm9zcyByb3dzIG9yIGNvbHVtbnM/CmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZykgKyAKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkgKyAKICBmYWNldF9ncmlkKGRydiB+IC4pCmdncGxvdChkYXRhID0gbXBnKSArIAogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKSArIAogIGZhY2V0X2dyaWQoLiB+IGRydikKYGBgCuyyqyDrsojsp7ggcGxvdOydgCBkcnbrpbwg7ZaJIOuwqe2WpeycvOuhnCBmYWNldO2VmOqzoCwg65GQIOuyiOynuCDtlIzroa/snYAgZHJ266W8IOyXtCDrsKntlqXsnLzroZwgZmFjZXTtlZzri6QuICAK65GQIHBsb3Qg7KSR7JeQ64qUIOyyq+uyiOynuCBwbG907J20IOu5hOq1kO2VmOq4sCDrjZQg7Im964ukLiAgCuyyqyDrsojsp7ggcGxvdOyXkOyEnOuKlCBkcnYg67OA7IiY7JeQIOuUsOudvCDtlokg67Cp7Zal7Jy866GcIO2MqOuEkOydtCDrgpjriZjslrQg6rCBIOq1rOuPmSDrsKnsi53rs4Qg642w7J207YSw6rCAIO2VnCDtlonsl5Ag7KCV66Cs65CY66+A66GcLCDsnITslYTrnpjroZwg67mE6rWQ7ZWY6riw6rCAIOuNlCDsp4HqtIDsoIHsnbTri6QuIOymiSwg6rCBIOq1rOuPmSDrsKnsi53snZgg7JeU7KeEIO2BrOq4sCDrtoTtj6zrpbwg7IiY7KeB7Jy866GcIOuwsOy5mO2VmOyXrCDruaDrpbTqsowg67mE6rWQ7ZWgIOyImCDsnojri6QuICAKPGJyPgoK67CY66m0LCDrkZAg67KI7Ke4IHBsb3TsnYAgZHJ266W8IOyXtCDrsKntlqXsnLzroZwg7Yyo64SQ7J2EIOuCmOuItOycvOuvgOuhnCwg7KKM7Jqw66GcIOu5hOq1kO2VtOyVvCDtlZzri6QuIOydtCDqsr3smrAsIOyLnOqwgeyggeycvOuhnCDruYTqtZDtlZjripQg642wIOuNlCDrp47snYAg7J2064+Z7J20IO2VhOyalO2VtCDsnbTtlbTtlZjquLDqsIAg67aI7Y647ZWgIOyImCDsnojri6QuICAKPGJyPgrsnITsnZgg7JiI7Iuc64qUIOu5hOq1kO2VmOugpOuKlCDrs4DsiJjrpbwg7ZaJIOuwqe2WpeycvOuhnCDrsLDsuZjtlZjripQg6rKD7J20IOyngeq0gOyggeydtOudvOuKlCDqsoPsnYQg67O07Jes7KSA64ukLiDtlZjsp4Drp4wgZmFjZXTrs4DsiJjqsIAg66eO6rGw64KYIOqwgOuhnOuhnCDrgpjsl7TtlZjripQg6rKD7J20IOuNlCDsoIHtlantlZwg6rK97Jqw64qUIOyXtOuwqe2WpeycvOuhnCDrsLDsuZjtlaAg7IiY64+EIOyeiOuLpC4g65Sw65287IScIOuzgOyImOulvCDtlonqs7wg7Je0IOykkSDslrTripAg67Cp7Zal7Jy866GcIOuwsOy5mO2VoOyngOuKlCDruYTqtZDtlZjroKTripQg642w7J207YSw7J2YIO2KueyEseqzvCDsi5zqsIHsoIHsnLzroZwg67mE6rWQ6rCAIOuNlCDsiazsmrQg67Cp7Zal7J2EIOqzoOugpO2VmOyXrCDqsrDsoJXtlbTslbwg7ZWc64ukLiAgCjxicj4KCiMjIyMgNy4gUmVjcmVhdGUgdGhpcyBwbG90IHVzaW5nIGZhY2V0X3dyYXAoKSBpbnN0ZWFkIG9mIGZhY2V0X2dyaWQoKS4gSG93IGRvIHRoZSBwb3NpdGlvbnMgb2YgdGhlIGZhY2V0IGxhYmVscyBjaGFuZ2U/CmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZykgKyAKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkgKwogIGZhY2V0X2dyaWQoZHJ2IH4gLikKYGBgCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZykgKyAKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkgKwogIGZhY2V0X3dyYXAofiBkcnYpCmBgYArsnITsnZgg6rK97JqwLCBmYWNldF93cmFwKCnsnYAg7Yyo64SQ7J2EIOyekOuPmeycvOuhnCDrsLDsuZjtlZjrqbAsIOqwgSDtjKjrhJDsnZgg652867KoIOychOy5mOqwgCDsnITsqr0g7KSR7JWZ7JeQIOuCmO2DgOuCnOuLpC4gIApmYWNldF9ncmlkKGRydiB+IC4p7JeQ7ISc7JmAIOuLrOumrCDtjKjrhJDsnbQg7ZaJIOuYkOuKlCDsl7TroZzrp4wg67Cw7LmY65CY7KeAIOyViuqzoCwg7IKs7Jqp7J6Q6rCAIOyngOygle2VnCBucm93IOuYkOuKlCBuY29s7JeQIOuUsOudvCDtjKjrhJAg67Cw7LmY66W8IOyhsOygle2VoCDsiJgg7J6I64ukLiAgCjxicj4KCiMjIDMuNi4xCiMjIyMgMS4gV2hhdCBnZW9tIHdvdWxkIHlvdSB1c2UgdG8gZHJhdyBhIGxpbmUgY2hhcnQ/IEEgYm94cGxvdD8gQSBoaXN0b2dyYW0/IEFuIGFyZWEgY2hhcnQ/CuyEoCDqt7jrnpjtlIQgKExpbmUgY2hhcnQpOiBnZW9tX2xpbmUoKSAgCuuwleyKpCDtlIzroa8gKEJveHBsb3QpOiBnZW9tX2JveHBsb3QoKSAgCu2eiOyKpO2GoOq3uOueqCAoSGlzdG9ncmFtKTogZ2VvbV9oaXN0b2dyYW0oKSAgCuuptOyggSDqt7jrnpjtlIQgKEFyZWEgY2hhcnQpOiBnZW9tX2FyZWEoKSAgCjxicj4KCiMjIyMgMi4gUnVuIHRoaXMgY29kZSBpbiB5b3VyIGhlYWQgYW5kIHByZWRpY3Qgd2hhdCB0aGUgb3V0cHV0IHdpbGwgbG9vayBsaWtlLiBUaGVuLCBydW4gdGhlIGNvZGUgaW4gUiBhbmQgY2hlY2sgeW91ciBwcmVkaWN0aW9ucy4K6rCBIGRyduuzhOuhnCDri6Trpbgg7IOJ7IOB7J2YIOygkOydtCDrgpjtg4Drgpjqs6AsIOqwgSDsg4nsg4Hsl5Ag7ZW064u57ZWY64qUIOqzoeyEoOydtCDstpTqsIDrkKAg6rKD7J2064ukLiAgCnNlID0gRkFMU0XripQg7Iug66KwIOq1rOqwhOydhCDtkZzsi5ztlZjsp4Ag7JWK64+E66GdIOyEpOygle2VtCBzbW1vdGgg6rOh7ISg7JeQIOyLoOuisCDqtazqsIQg7J2M7JiB7J20IOyXhuydhCDqsoPsnbTri6QuICAKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZywgbWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3ksIGNvbG9yID0gZHJ2KSkgKyAKICBnZW9tX3BvaW50KCkgKyAKICBnZW9tX3Ntb290aChzZSA9IEZBTFNFKQpgYGAKIyMjIyAzLiBXaGF0IGRvZXMgc2hvdy5sZWdlbmQgPSBGQUxTRSBkbz8gV2hhdCBoYXBwZW5zIGlmIHlvdSByZW1vdmUgaXQ/IFdoeSBkbyB5b3UgdGhpbmsgSSB1c2VkIGl0IGVhcmxpZXIgaW4gdGhlIGNoYXB0ZXI/ICAKc2hvdy5sZWdlbmQgPSBGQUxTReuKlCDrspTroYAobGVnZW5kKeulvCDsiKjquLTri6QuICAK7J206rKD7J2EIOygnOqxsO2VmOuptCDrspTroYDqsIAg64uk7IucIO2RnOyLnOuQnOuLpC4gIArssYXsl5DshJwg7J2066W8IOyCrOyaqe2VnCDsnbTsnKDripQg7Iuc6rCB7ZmU7JeQ7IScIOuylOuhgOqwgCDtlYTsmpTtlZjsp4Ag7JWK64uk6rOgIO2MkOuLqO2WiOqxsOuCmCDsi5zqsIHsnYQg64uo7Iic7ZmU7ZWY6riwIOychO2VtOyEnOydvCDqsoPsnbTri6QuICAKPGJyPgoKCiMjIyMgNC4gV2hhdCBkb2VzIHRoZSBzZSBhcmd1bWVudCB0byBnZW9tX3Ntb290aCgpIGRvPwpzZSA9IFRSVUXroZwg7ISk7KCV7ZWY66m0IHNtbW90aCDqs6HshKAg7KO87JyE7JeQIOyLoOuisCDqtazqsITsnYQg64KY7YOA64K064qUIOydjOyYgeydtCDstpTqsIDrkJzri6QuICAgCuq4sOuzuOqwkuydgCBUUlVF7J206rOgLCBzZSA9IEZBTFNF66GcIOyEpOygle2VmOuptCDsi6DrorAg6rWs6rCE7J2EIOygnOqxsO2VmOyXrCDqs6HshKDrp4wg7ZGc7Iuc65Cc64ukLiAgCjxicj4KCiMjIyMgNS4gV2lsbCB0aGVzZSB0d28gZ3JhcGhzIGxvb2sgZGlmZmVyZW50PyBXaHkvd2h5IG5vdD8KYGBge3J9CmdncGxvdChkYXRhID0gbXBnLCBtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpICsgCiAgZ2VvbV9wb2ludCgpICsgCiAgZ2VvbV9zbW9vdGgoKQpnZ3Bsb3QoKSArIAogIGdlb21fcG9pbnQoZGF0YSA9IG1wZywgbWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKSArIAogIGdlb21fc21vb3RoKGRhdGEgPSBtcGcsIG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkKYGBgCuuRkCDqt7jrnpjtlITripQg64+Z7J287ZWY6rKMIOuztOydvCDqsoPsnbTri6QuIOyZnOuDkO2VmOuptCDrkZAg7L2U65OcIOuqqOuRkCDrj5nsnbztlZwg642w7J207YSw66W8IOyCrOyaqe2VmOyXrCDqsJnsnYAg7IKw7KCQ64+E7JmAIHNtb290aCDqs6HshKDsnYQg6re466as6riwIOuVjOusuOydtOuLpC4gIArssqsg67KI7Ke4IOy9lOuTnOyXkOyEnOuKlCDrjbDsnbTthLDsmYAg66ek7ZWR7J2EIGdncGxvdCgpIO2VqOyImOyXkOyEnCDshKTsoJXtlojsp4Drp4wsIOuRkCDrsojsp7gg7L2U65Oc7JeQ7ISc64qUIOqwgeqwgeydmCBnZW9tIO2VqOyImOyXkOyEnCDshKTsoJXtlojsirXri4jri6QuIOyLnOqwgeyggeycvOuhnOuKlCDssKjsnbTqsIAg7JeG7Jy864KYLCDrkZAg67KI7Ke4IOuwqeuyleydtCDrjZQg66qF7Iuc7KCB7J2065286rOgIO2VoCDsiJgg7J6I64ukLiAgCjxicj4KCiMjIyMgNi4gUmVjcmVhdGUgdGhlIFIgY29kZSBuZWNlc3NhcnkgdG8gZ2VuZXJhdGUgdGhlIGZvbGxvd2luZyBncmFwaHMuIE5vdGUgdGhhdCB3aGVyZXZlciBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlIGlzIHVzZWQgaW4gdGhlIHBsb3QsIGl04oCZcyBkcnYuCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZywgbWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKSArIAogIGdlb21fcG9pbnQoY29sb3IgPSAiYmxhY2siKSArIAogIGdlb21fc21vb3RoKHNlID0gRkFMU0UpCgpgYGAKYGBge3J9CmdncGxvdChkYXRhID0gbXBnLCBtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpICsgCiAgZ2VvbV9wb2ludChjb2xvciA9ICJibGFjayIpICsgCiAgZ2VvbV9zbW9vdGgoYWVzKGdyb3VwID0gZHJ2KSwgY29sb3IgPSAiYmx1ZSIsIHNlID0gRkFMU0UpCmBgYApgYGB7cn0KZ2dwbG90KGRhdGEgPSBtcGcsIG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5LCBjb2xvciA9IGRydikpICsgCiAgZ2VvbV9wb2ludCgpICsgCiAgZ2VvbV9zbW9vdGgoc2UgPSBGQUxTRSkKYGBgCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZywgbWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3ksIGNvbG9yID0gZHJ2KSkgKyAKICBnZW9tX3BvaW50KCkgKyAKICBnZW9tX3Ntb290aChzZSA9IEZBTFNFLCBjb2xvciA9ICJibHVlIikKYGBgCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZywgbWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3ksIGNvbG9yID0gZHJ2KSkgKyAKICBnZW9tX3BvaW50KCkgKyAKICBnZW9tX3Ntb290aChhZXMobGluZXR5cGUgPSBkcnYpLCBzZSA9IEZBTFNFKQpgYGAKYGBge3J9CmdncGxvdChkYXRhID0gbXBnLCBtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSwgZmlsbCA9IGRydikpICsgCiAgZ2VvbV9wb2ludChzaXplID0gNCwgc2hhcGUgPSAyMSwgY29sb3IgPSAid2hpdGUiLCBzdHJva2UgPSAxLjUpCmBgYAojIyAzLjcuMQojIyMjIDEuIFdoYXQgaXMgdGhlIGRlZmF1bHQgZ2VvbSBhc3NvY2lhdGVkIHdpdGggc3RhdF9zdW1tYXJ5KCk/IEhvdyBjb3VsZCB5b3UgcmV3cml0ZSB0aGUgcHJldmlvdXMgcGxvdCB0byB1c2UgdGhhdCBnZW9tIGZ1bmN0aW9uIGluc3RlYWQgb2YgdGhlIHN0YXQgZnVuY3Rpb24/CnN0YXRfc3VtbWFyeSgp7J2YIOq4sOuzuCBnZW9t7J2AIGdlb21fcG9pbnRyYW5nZSgp7J2064ukLiBzdGF0X3N1bW1hcnkoKeuKlCDsmpTslb0g7Ya16rOE66W8IOq3uOumtCDrlYwg7IKs7Jqp65CY66mwLCDso7zroZwg7Y+J6reg6rO8IOyLoOuisCDqtazqsIQg65Ox7J2EIO2RnOyLnO2VoCDrlYwg7Jyg7Jqp7ZWY64ukLiAgCnN0YXRfc3VtbWFyeSgpIOuMgOyLoCDquLDrs7ggZ2VvbeydhCDsgqzsmqntlZjqs6Ag7Iu264uk66m0IGdlb21fcG9pbnRyYW5nZSgp66W8IOyCrOyaqe2VoCDsiJgg7J6I64ukLiAgCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzKSArICAKICBnZW9tX3BvaW50cmFuZ2UoCiAgICBtYXBwaW5nID0gYWVzKHggPSBjdXQsIHkgPSBkZXB0aCwgeW1pbiA9IC4ueW1pbi4uLCB5bWF4ID0gLi55bWF4Li4pLAogICAgc3RhdCA9ICJzdW1tYXJ5IiwKICAgIGZ1bi5taW4gPSBtaW4sCiAgICBmdW4ubWF4ID0gbWF4LAogICAgZnVuID0gbWVkaWFuCiAgKQpgYGAKYGBge3J9CmdncGxvdChkYXRhID0gZGlhbW9uZHMpICsKICBnZW9tX3BvaW50cmFuZ2UobWFwcGluZyA9IGFlcyAoeCA9IGN1dCwgeSA9IGRlcHRoLCB5bWluID1kZXB0aCwgeW1heCA9ZGVwdGgpKQpgYGAKCiMjIyMgMi4gV2hhdCBkb2VzIGdlb21fY29sKCkgZG8/IEhvdyBpcyBpdCBkaWZmZXJlbnQgdG8gZ2VvbV9iYXIoKT8KZ2VvbV9jb2woKeydgCB47LaV6rO8IHnstpXsnZgg6rCS7J2EIOq3uOuMgOuhnCDsgqzsmqntlbQg66eJ64yAIOq3uOuemO2UhOulvCDqt7jrprDri6QuIOymiSwg642w7J207YSwIO2UhOugiOyehCDrgrTsl5Ag7J2066+4IHnstpUg6rCS7J20IOyeiOuKlCDqsr3smrAgZ2VvbV9jb2woKeydhCDsgqzsmqntlZjsl6wg6re4IOqwkuydhCDqt7jrjIDroZwg66eJ64yA7J2YIOuGkuydtOuhnCDrgpjtg4Drgrjri6QuICAKCjxicj4KZ2VvbV9iYXIoKeuKlCB56rCS7J20IOyjvOyWtOyngOyngCDslYrsnLzrqbQg7J6Q64+Z7Jy866GcIOuNsOydtO2EsOydmCDqsJzsiJjrpbwg7IS47Ja0IOu5iOuPhCDrp4nrjIAg6re4656Y7ZSE66W8IOq3uOumsOuLpC4g7KaJLCBnZW9tX2Jhcigp64qUIHN0YXQgPSAiY291bnQi6rCAIOq4sOuzuCDshKTsoJXsnbTrqbAsIOuzhOuPhOydmCB56rCS7J20IO2VhOyalO2VmOyngCDslYrri6QuICAKCjxicj4gIAoKCiMjIyMgZ2VvbV9jb2woKeydgCB57LaVIOqwkuydhCDqt7jrjIDroZwg7IKs7Jqp7ZWY6rOgLCBnZW9tX2Jhcigp64qUIHnstpUg6rCS7J2EIOuUsOuhnCDsp4DsoJXtlZjsp4Ag7JWK7Jy866m0IOyekOuPmeycvOuhnCDqsJzsiJjrpbwg7IS864ukLiAgCgo8YnI+CgojIyMjIDMuIE1vc3QgZ2VvbXMgYW5kIHN0YXRzIGNvbWUgaW4gcGFpcnMgdGhhdCBhcmUgYWxtb3N0IGFsd2F5cyB1c2VkIGluIGNvbmNlcnQuIFJlYWQgdGhyb3VnaCB0aGUgZG9jdW1lbnRhdGlvbiBhbmQgbWFrZSBhIGxpc3Qgb2YgYWxsIHRoZSBwYWlycy4gV2hhdCBkbyB0aGV5IGhhdmUgaW4gY29tbW9uPwrrjIDrtoDrtoTsnZggZ2VvbeqzvCBzdGF07J2AIHBhaXLsnYQg7J2066Oo66mwLCDrj5nsnbztlZwg642w7J207YSwIOuzgO2ZmOqzvCDsi5zqsIHtmZQg66qp7KCB7J2EIOqwgOyngOqzoCDsnojri6QuIOyYiOulvCDrk6TslrQsIGdlb21fYmFyKCnripQgc3RhdF9jb3VudCgp7JmAIO2VqOq7mCDsgqzsmqnrkJjrqbAsIGdlb21fc21vb3RoKCnripQgc3RhdF9zbW9vdGgoKeyZgCDtlajqu5gg7IKs7Jqp65Cc64ukLiAKPGJyPgogIOyjvOyalOyYiOyLnDogIAogIAogICAgZ2VvbV9iYXIoKSDihpQgc3RhdF9jb3VudCgpICAKICAgIGdlb21fY29sKCkg4oaUIHN0YXRfaWRlbnRpdHkoKQogICAgZ2VvbV9oaXN0b2dyYW0oKSDihpQgc3RhdF9iaW4oKQogICAgZ2VvbV9kZW5zaXR5KCkg4oaUIHN0YXRfZGVuc2l0eSgpCiAgICBnZW9tX2JveHBsb3QoKSDihpQgc3RhdF9ib3hwbG90KCkKICAgIGdlb21fc21vb3RoKCkg4oaUIHN0YXRfc21vb3RoKCkKICAgIGdlb21fcG9pbnQoKSDihpQgc3RhdF9pZGVudGl0eSgpCiAgICBnZW9tX2xpbmUoKSDihpQgc3RhdF9pZGVudGl0eSgpCiAgICBnZW9tX3Zpb2xpbigpIOKGlCBzdGF0X3lkZW5zaXR5KCkKICAgIGdlb21fcG9pbnRyYW5nZSgpIOKGlCBzdGF0X3N1bW1hcnkoKQogICAgZ2VvbV9lcnJvcmJhcigpIOKGlCBzdGF0X3N1bW1hcnkoKQogICAgZ2VvbV9yaWJib24oKSDihpQgc3RhdF9zdW1tYXJ5KCkKICAgIApodHRwOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS8g7ZW064u5IOunge2BrOulvCDssLjqs6DtlaAg7IiYIOyeiOuLpC4gIAoKPGJyPgoKIyMjIyA0LiBXaGF0IHZhcmlhYmxlcyBkb2VzIHN0YXRfc21vb3RoKCkgY29tcHV0ZT8gV2hhdCBwYXJhbWV0ZXJzIGNvbnRyb2wgaXRzIGJlaGF2aW91cj8gIAoKICBzdGF0X3Ntb290aCgp7J2YIOyjvOyalCB2YXJpYWJsZXM6ICAKICAKICAgIHkKICAgIHltaW4KICAgIHltYXgKCiAg7KO87JqUIHBhcmFtZXRlcjogIAogIAogICAgbWV0aG9kOiBzbW9vdGjtlajsiJjrpbwg7ISg7YOd7ZWY64qUIOyYteyFmAogICAgc2U6IOyLoOuisCDqtazqsITsnYQg7ZGc7Iuc7ZWg7KeAIOyXrOu2gOulvCDqsrDsoJXtlZjrqbAsIFRSVUUg65iQ64qUIEZBTFNF66GcIOyEpOyglQogICAgbGV2ZWw6IOyLoOuisCDqtazqsITsnZgg7IiY7KSA7J2EIOyEpOygle2VqeuLiOuLpCAo7JiIOiAwLjk164qUIDk1JSDsi6DrorAg6rWs6rCEKS4KICAgIHNwYW46IG1ldGhvZCA9ICJsb2VzcyLsnbwg65WMIOqzoeyEoOydmCBzbW9vdGhuZXNz7J2EIOygnOyWtC4KYGBge3J9CmdncGxvdChkYXRhID0gbXBnLCBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gZHJ2KSkgKwogIHN0YXRfc21vb3RoKHNlID0gRkFMU0UpCmBgYAojIyMjIDUuIEluIG91ciBwcm9wb3J0aW9uIGJhciBjaGFydCwgd2UgbmVlZCB0byBzZXQgZ3JvdXAgPSAxLiBXaHk/IEluIG90aGVyIHdvcmRzIHdoYXQgaXMgdGhlIHByb2JsZW0gd2l0aCB0aGVzZSB0d28gZ3JhcGhzPwpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcykgKyAKICBnZW9tX2JhcihtYXBwaW5nID0gYWVzKHggPSBjdXQsIHkgPSBhZnRlcl9zdGF0KHByb3ApKSkKZ2dwbG90KGRhdGEgPSBkaWFtb25kcykgKyAKICBnZW9tX2JhcihtYXBwaW5nID0gYWVzKHggPSBjdXQsIGZpbGwgPSBjb2xvciwgeSA9IGFmdGVyX3N0YXQocHJvcCkpKQpgYGAK67mE7JyoIOunieuMgCDqt7jrnpjtlITsl5DshJwgZ3JvdXAgPSAx7J2EIOyEpOygle2VmOuKlCDsnbTsnKDripQgeOy2leydmCDqsIEg67KU7KO86rCAIOuPheumveyggeyduCDqt7jro7nsnLzroZwg6rCE7KO865CY64+E66GdIO2VmOq4sCDsnITtlajsnbTri6QuIGdyb3VwID0gMeydhCDshKTsoJXtlZjsp4Ag7JWK7Jy866m0IOqwgSDrspTso7zqsIAg6rCc67OE7KCB7Jy866GcIOqzhOyCsOuQmOyngCDslYrquLAg65WM66y47JeQIOybkO2VmOuKlCDruYTsnKjsnbQg64KY7YOA64KY7KeAIOyViuqyjCDrkJzri6QuICAKCuychCDrkZAg6re4656Y7ZSE7J2YIOusuOygnOygkOydgCDruYTsnKjsnbQg7KCc64yA66GcIOqzhOyCsOuQmOyngCDslYrripTri6TripQg6rKD7J207J2064ukLuydtCDrkZAg6re4656Y7ZSE7JeQ7ISc64qUIGdyb3VwID0gMeydhCDshKTsoJXtlZjsp4Ag7JWK7JWEIOqwgSBjdXTsnZgg6rCS65Ok7J20IOuzhOuPhOuhnCDqt7jro7ntmZTrkJjsp4Ag7JWK7JWY64ukLiDqsrDqs7zsoIHsnLzroZwgcHJvcOydtCDsoITssrQg642w7J207YSw66W8IOuMgOyDgeycvOuhnCDqs4TsgrDrkJjslrQsIOqwgSBjdXTsnZgg67mE7Jyo7J20IOyYrOuwlOultOqyjCDtkZzsi5zrkJjsp4Ag7JWK64qU64qU64ukLiBncm91cCA9IDHsnYQg7LaU6rCA7ZWY7JesIOqwgSBjdXQg67KU7KO86rCAIOuPheumveyggeyduCDqt7jro7nsnLzroZwg6rOE7IKw65CY64+E66GdIO2VtOyVvCDsmKzrsJTrpbgg67mE7Jyo7J20IO2RnOyLnOuQnOuLpC4gIAoKIyMgMy44LjEKIyMjIyAxLiBXaGF0IGlzIHRoZSBwcm9ibGVtIHdpdGggdGhpcyBwbG90PyBIb3cgY291bGQgeW91IGltcHJvdmUgaXQ/CgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBtcGcsIG1hcHBpbmcgPSBhZXMoeCA9IGN0eSwgeSA9IGh3eSkpICsgCiAgZ2VvbV9wb2ludCgpCmBgYAogIAogIAogICAKICAgIOusuOygnOygkDog7Jes65+sIOuNsOydtO2EsCDtj6zsnbjtirjqsIAg64+Z7J287ZWcIOychOy5mOyXkCDsnojslrTshJwg6rK57LmY6riwIOuVjOusuOyXkCDrqqjrk6Ag642w7J207YSw66W8IO2ZleyduO2VoCDsiJgg7JeG64ukLiDqsrnsuZwg7KCQ65Ok7J20IOunjuyVhCDsi6TsoJwg642w7J207YSw67O064ukIOyggeydgCDsoJDrk6TsnbQg67O07J206rKMIOuQnOuLpC4KICAgIOqwnOyEoOuwqeuylTogZ2VvbV9qaXR0ZXIoKeulvCDsgqzsmqntlZjsl6wg7KCQ65Ok7J2EIOyVveqwhOyUqSDtnanslrTsp4Dqsowg7ZWY66m0IOqyuey5mOuKlCDrrLjsoJzrpbwg7KSE7J206rOgIOuNsOydtO2EsOulvCDrjZQg7J6YIOyLnOqwge2ZlO2VoCDsiJgg7J6I64ukLiAgCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZywgbWFwcGluZyA9IGFlcyh4ID0gY3R5LCB5ID0gaHd5KSkgKwogIGdlb21faml0dGVyKCkKCmBgYAojIyMjIDIuIFdoYXQgcGFyYW1ldGVycyB0byBnZW9tX2ppdHRlcigpIGNvbnRyb2wgdGhlIGFtb3VudCBvZiBqaXR0ZXJpbmc/Cmdlb21faml0dGVyKCnsl5DshJwg7KCQ65Ok7J20IO2dqeyWtOyngOuKlCDsoJXrj4TripQgd2lkdGjsmYAgaGVpZ2h0IO2MjOudvOuvuO2EsOuhnCDsoJzslrTtlaAg7IiYIOyeiOuLpC4gIApgYGB7cn0KZ2dwbG90KGRhdGEgPSBtcGcsIG1hcHBpbmcgPSBhZXMoeCA9IGN0eSwgeSA9IGh3eSkpICsgCiAgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjIsIGhlaWdodCA9IDAuMikKYGBgCgojIyMjIDMuIENvbXBhcmUgYW5kIGNvbnRyYXN0IGdlb21faml0dGVyKCkgd2l0aCBnZW9tX2NvdW50KCkKYGBge3J9CiMgZ2VvbSBqaXR0ZXIKZ2dwbG90KGRhdGEgPSBtcGcsIG1hcHBpbmcgPSBhZXMoeCA9IGN0eSwgeSA9IGh3eSkpICsKICBnZW9tX2ppdHRlcigpCmBgYApgYGB7cn0KIyBnZW9tIGNvdW50CmdncGxvdChkYXRhID0gbXBnLCBtYXBwaW5nID0gYWVzKHggPSBjdHksIHkgPSBod3kpKSArCiAgZ2VvbV9jb3VudCgpCmBgYAogIAogIAogICAgZ2VvbV9qaXR0ZXIoKTog7KCQ65Ok7J2EIOyVveqwhOyUqSDtnanslrTsp4Dqsowg7ZWY7JesIOyLnOqwge2ZlO2VnOuLpC4g6rCBIOuNsOydtO2EsCDtj6zsnbjtirjripQg7JW96rCE7J2YIOuzgOychOqwgCDsoIHsmqnrkJjslrQg7ZGc7Iuc65Cc64ukLgogICAgZ2VvbV9jb3VudCgpOiDrj5nsnbztlZwg7JyE7LmY7JeQIOyXrOufrCDrjbDsnbTthLAg7Y+s7J247Yq46rCAIOyeiOydhCDqsr3smrAsIOygkOydmCDtgazquLDrpbwg6re4IOqwnOyImOyXkCDruYTroYDtlZjsl6wg7ZGc7Iuc7ZWc64ukLiDqsJnsnYAg7JyE7LmY7JeQIOunjuydgCDrjbDsnbTthLDqsIAg7J6I7J2E7IiY66GdIOygkOydtCDsu6Tsp4Tri6QuICAKICAgIArspoksIGdlb21faml0dGVyKCnripQg7KCQ65Ok7J2EIO2dqeyWtOyngOqyjO2VtCDrjbDsnbTthLDrpbwg67aE66as7ZWY7KeA66eMLCBnZW9tX2NvdW50KCnripQg7KCQ7J2YIO2BrOq4sOuhnCDrjbDsnbTthLDsnZgg7KSR67O17J2EIO2RnO2YhO2VnOuLpC4KCiMjIyMgNC4gV2hhdOKAmXMgdGhlIGRlZmF1bHQgcG9zaXRpb24gYWRqdXN0bWVudCBmb3IgZ2VvbV9ib3hwbG90KCk/IENyZWF0ZSBhIHZpc3VhbGlzYXRpb24gb2YgdGhlIG1wZyBkYXRhc2V0IHRoYXQgZGVtb25zdHJhdGVzIGl0LgpnZW9tX2JveHBsb3QoKeydmCDquLDrs7gg7JyE7LmYIOyhsOyglSDrsKnsi53snYAgcG9zaXRpb24gPSAiZG9kZ2Ui7J2064ukLiDsnbQg7JyE7LmYIOyhsOyglSDrsKnsi53snYAg6rCBIOuylOyjvOyXkCDrjIDtlbQg67CV7IqkIO2UjOuhr+ydhCDrgpjrnoDtnogg67Cw7LmY7ZWY7JesIOyEnOuhnCDqsrnsuZjsp4Ag7JWK64+E66GdIO2VnOuLpC4KYGBge3J9CmdncGxvdChkYXRhID0gbXBnLCBtYXBwaW5nID0gYWVzKHggPSBkcnYsIHkgPSBod3ksIGZpbGwgPSBkcnYpKSArIAogIGdlb21fYm94cGxvdCgpCgpgYGAKCiMjIDMuOS4xCiMjIyMgMS4gVHVybiBhIHN0YWNrZWQgYmFyIGNoYXJ0IGludG8gYSBwaWUgY2hhcnQgdXNpbmcgY29vcmRfcG9sYXIoKS4KYGBge3J9CmdncGxvdChkYXRhID0gZGlhbW9uZHMsIGFlcyh4ID0gZmFjdG9yKDEpLCBmaWxsID0gY3V0KSkgKwogIGdlb21fYmFyKHdpZHRoID0gMSkgKwogIGNvb3JkX3BvbGFyKHRoZXRhID0gInkiKSArCiAgbGFicyh4ID0gTlVMTCwgeSA9IE5VTEwpCmBgYAojIyMjIDIuIFdoYXQgZG9lcyBsYWJzKCkgZG8/IFJlYWQgdGhlIGRvY3VtZW50YXRpb24uCmxhYnMoKSDripQg6re4656Y7ZSE7J2YIOugiOydtOu4lOydhCDshKTsoJXtlZjripQg7ZWo7IiY66GcLCDstpUg7KCc66qpLCDqt7jrnpjtlIQg7KCc66qpLCDrspTroYAg7KCc66qpIOuTseydhCDrs4Dqsr3tlaAg7IiYIOyeiOuLpC4gIApsYWJzKCnripQgdGl0bGUsIHN1YnRpdGxlLCBjYXB0aW9uLCB4LCB5LCBmaWxsLCBjb2xvciDrk7HsnZgg66ek6rCc67OA7IiY66W8IOyCrOyaqe2VtCDqt7jrnpjtlITsnZgg64uk7JaR7ZWcIOyalOyGjOyXkCDsoJzrqqnsnYQg7LaU6rCA7ZWY6rGw64KYIOuzgOqyve2VoCDsiJgg7J6I64ukLgo8YnI+CgojIyMjIDMuIFdoYXTigJlzIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gY29vcmRfcXVpY2ttYXAoKSBhbmQgY29vcmRfbWFwKCk/ICAKCiAgICBjb29yZF9xdWlja21hcCgpOiDsp4Drj4Trpbwg6re466a0IOuVjCDsp4DrpqzsoIEg67mE7Jyo7J2EIOu5oOultOqyjCDshKTsoJXtlZzri6QuIOqzhOyCsOydhCDri6jsiJztmZTtlZjsl6wg67mE7Jyo7J20IOygle2Zle2eiCDrp57sp4Qg7JWK7KeA66eMLCDsho3rj4TqsIAg67mg66W064ukLiDsnbzrsJjsoIHsnLzroZwg7KeA64+E6rCAIO2ZlOuptOyXkCDsoIHsoIjtlZwg67mE7Jyo66GcIOuztOydtOuPhOuhnSDsobDsoJXtlZzri6Qu7KeB7ISg7J20IOycoOyngOuQmOyngCDslYrripTri6QuCiAgICBjb29yZF9tYXAoKTog7KeA66as7KCBIOu5hOycqOydhCDsoJXtmZXtlZjqsowg7KGw7KCV7ZWY66mwLCDri6TslpHtlZwg7Yis7JiB67KV7J2EIOyCrOyaqe2VoCDsiJgg7J6I7Iq164uI64ukLiDqs4TsgrDsnbQg642UIOuzteyeoe2VmOqzoCDsi5zqsITsnbQg7Jik656YIOqxuOumrOyngOunjCwg67mE6rWQ7KCBIOygle2Zle2VnCDsp4DrpqzsoIEg67mE7Jyo7J2EIOygnOqzte2VnOuLpC4g7KeB7ISg7J2EICjrjIDrtoDrtoQpIOycoOyngO2VnOuLpC4gIAogICAgCiAgPGJyPgogIAojIyMjIDQuIFdoYXQgZG9lcyB0aGUgcGxvdCBiZWxvdyB0ZWxsIHlvdSBhYm91dCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gY2l0eSBhbmQgaGlnaHdheSBtcGc/IFdoeSBpcyBjb29yZF9maXhlZCgpIGltcG9ydGFudD8gV2hhdCBkb2VzIGdlb21fYWJsaW5lKCkgZG8/CmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZywgbWFwcGluZyA9IGFlcyh4ID0gY3R5LCB5ID0gaHd5KSkgKwogIGdlb21fcG9pbnQoKSArIAogIGdlb21fYWJsaW5lKCkgKwogIGNvb3JkX2ZpeGVkKCkKYGBgCiAg7J20IHBsb3TsnYAgY3R5KOuPhOyLnCDsl7DruYQp7JmAIGh3eSjqs6Dsho3rj4TroZwg7Jew67mEKSDsgqzsnbTsl5Ag7JaR7J2YIOyDgeq0gCDqtIDqs4TqsIAg7J6I7J2M7J2EIOuztOyXrOykgOuLpC4g7KaJLCBjdHnqsIAg64aS7J2E7IiY66GdIGh3eeuPhCDrhpLslYTsp4DripQg6rK97Zal7J20IOyeiOuLpC4gIAogIAogICAgY29vcmRfZml4ZWQoKeydmCDspJHsmpTshLE6IGNvb3JkX2ZpeGVkKCnripQgeOy2leqzvCB57LaV7J2YIOu5hOycqOydhCAxOjHroZwg6rOg7KCV7ZWY7JesLCDrkZAg7LaV7J2YIOuLqOychOqwgCDrj5nsnbztlZwg67mE7Jyo66GcIO2RnOyLnOuQmOuPhOuhnSDtlZzri6QuIOydtOulvCDthrXtlbQgZ2VvbV9hYmxpbmUoKeyXkOyEnCDstpTqsIDtlZwg64yA6rCB7ISgKOq4sOyauOq4sCAx7J2YIOyEoCnsnbQg7KCV7ZmV7Z6IIDQ164+EIOqwgeuPhOuhnCDrgpjtg4DrgpjrqbAsIGN0eeyZgCBod3nsnZgg6rSA6rOE66W8IOuNlCDsp4HqtIDsoIHsnLzroZwg7J207ZW07ZWgIOyImCDsnojri6QuIOu5hOycqOydtCDqs6DsoJXrkJjsp4Ag7JWK7Jy866m0LCDstpXsnZgg67mE7JyoIOywqOydtOuhnCDsnbjtlbQg6rSA6rOE6rCAIOyZnOqzoeuQoCDsiJgg7J6I64ukLgogICAgZ2VvbV9hYmxpbmUoKeydmCDsl63tlaA6IGdlb21fYWJsaW5lKCnsnYAg6riw67O47KCB7Jy866GcIOq4sOyauOq4sOqwgCAx7J206rOgIOygiO2OuOydtCAw7J24IOuMgOqwgeyEoOydhCDstpTqsIDtlZjsl6wsIHjsmYAgeeqwgCDqsJnsnYAg6rCS7J28IOuVjOydmCDquLDspIDshKDsnYQg64KY7YOA64K464ukLiDsnbQg7ISg7J2EIOq4sOykgOycvOuhnCDrjbDsnbTthLDrpbwg67mE6rWQ7ZWgIOyImCDsnojsnLzrqbAsIOydtCDqsr3smrAgY3R57JmAIGh3eeydmCDqtIDqs4Trpbwg7Y+J6rCA7ZWY64qUIOuNsCDrj4Tsm4DsnYQg7KSA64ukLiAgCiAgPGJyPgogIAojIyA1LjIuNAojIyMjIDEuIEZpbmQgYWxsIGZsaWdodHMgdGhhdAoKICBIYWQgYW4gYXJyaXZhbCBkZWxheSBvZiB0d28gb3IgbW9yZSBob3VycyAgCiAgCiAgRmxldyB0byBIb3VzdG9uIChJQUggb3IgSE9VKSAgCiAgCiAgV2VyZSBvcGVyYXRlZCBieSBVbml0ZWQsIEFtZXJpY2FuLCBvciBEZWx0YSAgCiAgCiAgRGVwYXJ0ZWQgaW4gc3VtbWVyIChKdWx5LCBBdWd1c3QsIGFuZCBTZXB0ZW1iZXIpICAKICAKICBBcnJpdmVkIG1vcmUgdGhhbiB0d28gaG91cnMgbGF0ZSwgYnV0IGRpZG7igJl0IGxlYXZlIGxhdGUgIAogIAogIFdlcmUgZGVsYXllZCBieSBhdCBsZWFzdCBhbiBob3VyLCBidXQgbWFkZSB1cCBvdmVyIDMwIG1pbnV0ZXMgaW4gZmxpZ2h0ICAKICAKICAKYGBge3J9CmxpYnJhcnkobnljZmxpZ2h0czEzKQpsaWJyYXJ5KGRwbHlyKQoKZmxpZ2h0cyAlPiUKICBmaWx0ZXIoYXJyX2RlbGF5ID49IDEyMCwgICMgSGFkIGFuIGFycml2YWwgZGVsYXkgb2YgdHdvIG9yIG1vcmUgaG91cnMKICAgICAgICAgZGVzdCAlaW4lIGMoIklBSCIsICJIT1UiKSwgICMgRmxldyB0byBIb3VzdG9uIChJQUggb3IgSE9VKQogICAgICAgICBjYXJyaWVyICVpbiUgYygiVUEiLCAiQUEiLCAiREwiKSwgICMgV2VyZSBvcGVyYXRlZCBieSBVbml0ZWQsIEFtZXJpY2FuLCBvciBEZWx0YQogICAgICAgICBtb250aCAlaW4lIGMoNywgOCwgOSksICAjIERlcGFydGVkIGluIHN1bW1lciAoSnVseSwgQXVndXN0LCBhbmQgU2VwdGVtYmVyKQogICAgICAgICBhcnJfZGVsYXkgPiBkZXBfZGVsYXksICAjIEFycml2ZWQgbW9yZSB0aGFuIHR3byBob3VycyBsYXRlLCBidXQgZGlkbuKAmXQgbGVhdmUgbGF0ZQogICAgICAgICBkZXBfZGVsYXkgPj0gNjAsIGFycl9kZWxheSAtIGRlcF9kZWxheSA+IDMwKSAgIyBXZXJlIGRlbGF5ZWQgYnkgYXQgbGVhc3QgYW4gaG91ciwgYnV0IG1hZGUgdXAgb3ZlciAzMCBtaW51dGVzIGluIGZsaWdodApgYGAKPGJyPgoKIyMjIyAyLiBTb3J0IGZsaWdodHMgdG8gZmluZCB0aGUgZmxpZ2h0cyB3aXRoIGxvbmdlc3QgZGVwYXJ0dXJlIGRlbGF5cy4gRmluZCB0aGUgZmxpZ2h0cyB0aGF0IGxlZnQgZWFybGllc3QgaW4gdGhlIG1vcm5pbmcuCiAg6rCA7J6lIOq4tCDstpzrsJwg7KeA7Jew7J20IOyeiOuKlCDtla3qs7XtjrgKYGBge3J9CmZsaWdodHMgJT4lCiAgYXJyYW5nZShkZXNjKGRlcF9kZWxheSkpCmBgYAogIOqwgOyepSDsnbTrpbgg7Iuc6rCE7JeQIOy2nOuwnO2VnCDtla3qs7XtjrgKYGBge3J9CmZsaWdodHMgJT4lCiAgYXJyYW5nZShkZXBfdGltZSkgJT4lCiAgZmlsdGVyKCFpcy5uYShkZXBfdGltZSkpICAKYGBgCiMjIyMgMy4gU29ydCBmbGlnaHRzIHRvIGZpbmQgdGhlIGZhc3Rlc3QgZmxpZ2h0cyAoSGludDogdHJ5IHNvcnRpbmcgYnkgYSBjYWxjdWxhdGlvbikuCgrruYTtlokg7IaN64+E66W8IOq4sOykgOycvOuhnCDsoJXroKztlaAg7IiYIOyeiOuLpC4g7IaN64+E64qUIGRpc3RhbmNlKOqxsOumrCnrpbwgYWlyX3RpbWUo67mE7ZaJIOyLnOqwhCnsnLzroZwg64KY64iE7Ja0IOqzhOyCsO2VoCDsiJgg7J6I64ukLiAKYGBge3J9CmZsaWdodHMgJT4lCiAgbXV0YXRlKHNwZWVkID0gZGlzdGFuY2UgLyAoYWlyX3RpbWUgLyA2MCkpICU+JQogIGFycmFuZ2UoZGVzYyhzcGVlZCkpCgpgYGAKIyMjIyA0LiBXaGljaCBmbGlnaHRzIHRyYXZlbGxlZCB0aGUgZmFydGhlc3Q/IFdoaWNoIHRyYXZlbGxlZCB0aGUgc2hvcnRlc3Q/CiAg6rCA7J6lIOupgOumrCDsnbTrj5ntlZwg7ZWt6rO17Y64CmBgYHtyfQpmbGlnaHRzICU+JQogIGFycmFuZ2UoZGVzYyhkaXN0YW5jZSkpCmBgYAogIOqwgOyepSDsp6fqsowg7J2064+Z7ZWcIO2Vreqzte2OuAoKYGBge3J9CmZsaWdodHMgJT4lCiAgYXJyYW5nZShkaXN0YW5jZSkKYGBgCiMjIyMgNS4gRG9lcyBpdCBtYXR0ZXIgd2hhdCBvcmRlciB5b3UgdXNlZCBmaWx0ZXIoKSBhbmQgYXJyYW5nZSgpIGluIGlmIHlvdeKAmXJlIHVzaW5nIGJvdGg/IFdoeS93aHkgbm90PyBUaGluayBhYm91dCB0aGUgcmVzdWx0cyBhbmQgaG93IG11Y2ggd29yayB0aGUgZnVuY3Rpb25zIHdvdWxkIGhhdmUgdG8gZG8uCmZpbHRlcigp7JmAIGFycmFuZ2UoKeydmCDsiJzshJzqsIAg7KSR7JqU7ZWgIOyImCDsnojri6QuICAKZmlsdGVyKCnroZwg66i87KCAIOuNsOydtO2EsOulvCDspITsnbTrqbQgYXJyYW5nZSgp6rCAIOygleugrO2VtOyVvCDtlaAg642w7J207YSwIOyWkeydtCDspITslrTrk6TslrQg7J6R7JeFIOyGjeuPhOqwgCDruajrnbzsp4gg7IiYIOyeiOuLpC4gIAphcnJhbmdlKCnrpbwg66i87KCAIOyCrOyaqe2VmOuptCDsoITssrQg642w7J207YSw7IWL7J2EIOygleugrO2VnCDtm4Qg7ZWE7YSw66eB7ZWY66+A66GcIOu2iO2VhOyalO2VnCDqs4TsgrDsnbQg67Cc7IOd7ZWgIOyImCDsnojri6QuICAKCuuUsOudvOyEnCwg7J2867CY7KCB7Jy866GcIGZpbHRlcigp66W8IOuovOyggCDsgqzsmqntlZjsl6wg642w7J207YSw66W8IOykhOyduCDtm4QgYXJyYW5nZSgp66W8IOyCrOyaqe2VmOuKlCDqsoPsnbQg642UIO2aqOycqOyggeydtOuLpC4gIAo8YnI+CgojIyA1LjMuNQojIyMjIDEuIEN1cnJlbnRseSBkZXBfdGltZSBhbmQgc2NoZWRfZGVwX3RpbWUgYXJlIGNvbnZlbmllbnQgdG8gbG9vayBhdCwgYnV0IGhhcmQgdG8gY29tcHV0ZSB3aXRoIGJlY2F1c2UgdGhleeKAmXJlIG5vdCByZWFsbHkgY29udGludW91cyBudW1iZXJzLiBDb252ZXJ0IHRoZW0gdG8gYSBtb3JlIGNvbnZlbmllbnQgcmVwcmVzZW50YXRpb24gb2YgbnVtYmVyIG9mIG1pbnV0ZXMgc2luY2UgbWlkbmlnaHQuCmBgYHtyfQpmbGlnaHRzIDwtIGZsaWdodHMgJT4lCiAgbXV0YXRlKAogICAgZGVwX3RpbWVfbWlucyA9IChkZXBfdGltZSAlLyUgMTAwKSAqIDYwICsgKGRlcF90aW1lICUlIDEwMCksCiAgICBzY2hlZF9kZXBfdGltZV9taW5zID0gKHNjaGVkX2RlcF90aW1lICUvJSAxMDApICogNjAgKyAoc2NoZWRfZGVwX3RpbWUgJSUgMTAwKQogICkKCmZsaWdodHMKCmBgYAojIyMjIDIuIENvbXBhcmUgYWlyX3RpbWUgd2l0aCBhcnJfdGltZSAtIGRlcF90aW1lLiBXaGF0IGRvIHlvdSBleHBlY3QgdG8gc2VlPyBXaGF0IGRvIHlvdSBzZWU/IFdoYXQgZG8geW91IG5lZWQgdG8gZG8gdG8gZml4IGl0PwphaXJfdGltZeydgCDsi6TsoJwg67mE7ZaJIOyLnOqwhCjrtoQg64uo7JyEKeydhCDrgpjtg4Drgrjri6QuICAK67CY66m0LCBhcnJfdGltZSAtIGRlcF90aW1l7J2AIOy2nOuwnCDsi5zqsITqs7wg64+E7LCpIOyLnOqwhOydhCDrubzshJwg7Ja77J2AIOqwkuydtOuvgOuhnCDsoJXtmZXtlZwg67mE7ZaJIOyLnOqwhOydhCDrgpjtg4DrgrTsp4Ag7JWK7J2EIOyImCDsnojri6QuICAK65Sw65287IScIGFycl90aW1l6rO8IGRlcF90aW1l64+EIEhITU0g7ZiV7Iud7J2066+A66GcLCDsnbQg6rCS7J2EIOu2hOycvOuhnCDrs4DtmZjtlbTslbwg7ZWc64ukLgpgYGB7cn0KZmxpZ2h0cyA8LSBmbGlnaHRzICU+JQogIG11dGF0ZSgKICAgIGRlcF90aW1lX21pbnMgPSAoZGVwX3RpbWUgJS8lIDEwMCkgKiA2MCArIChkZXBfdGltZSAlJSAxMDApLAogICAgYXJyX3RpbWVfbWlucyA9IChhcnJfdGltZSAlLyUgMTAwKSAqIDYwICsgKGFycl90aW1lICUlIDEwMCksCiAgICBhaXJfdGltZV9jYWxjdWxhdGVkID0gYXJyX3RpbWVfbWlucyAtIGRlcF90aW1lX21pbnMKICApCgojIOu5hOq1kApmbGlnaHRzICU+JQogIHNlbGVjdChhaXJfdGltZSwgYWlyX3RpbWVfY2FsY3VsYXRlZCkKCmBgYAojIyMjIDMuIENvbXBhcmUgZGVwX3RpbWUsIHNjaGVkX2RlcF90aW1lLCBhbmQgZGVwX2RlbGF5LiBIb3cgd291bGQgeW91IGV4cGVjdCB0aG9zZSB0aHJlZSBudW1iZXJzIHRvIGJlIHJlbGF0ZWQ/CmRlcF9kZWxheeuKlCBkZXBfdGltZeqzvCBzY2hlZF9kZXBfdGltZeydmCDssKjsnbTrpbwg64KY7YOA64K064qUIOqwkuycvOuhnCwg7IiY7ZWZ7KCB7Jy866Gc64qUIGRlcF9kZWxheSA9IGRlcF90aW1lIC0gc2NoZWRfZGVwX3RpbWXsnbTquLAg65WM66y47JeQIOqwmeydhCDqsoPsnbTrnbzqs6Ag7JiI7IOB7ZWgIOyImCDsnojri6QuCmBgYHtyfQpmbGlnaHRzIDwtIGZsaWdodHMgJT4lCiAgbXV0YXRlKAogICAgZGVwX3RpbWVfbWlucyA9IChkZXBfdGltZSAlLyUgMTAwKSAqIDYwICsgKGRlcF90aW1lICUlIDEwMCksCiAgICBzY2hlZF9kZXBfdGltZV9taW5zID0gKHNjaGVkX2RlcF90aW1lICUvJSAxMDApICogNjAgKyAoc2NoZWRfZGVwX3RpbWUgJSUgMTAwKSwKICAgIGRlcF9kZWxheV9jYWxjdWxhdGVkID0gZGVwX3RpbWVfbWlucyAtIHNjaGVkX2RlcF90aW1lX21pbnMKICApCgojIOqysOqzvCDruYTqtZAKZmxpZ2h0cyAlPiUKICBzZWxlY3QoZGVwX2RlbGF5LCBkZXBfZGVsYXlfY2FsY3VsYXRlZCkKCmBgYAojIyMjIDQuIEJyYWluc3Rvcm0gYXMgbWFueSB3YXlzIGFzIHBvc3NpYmxlIHRvIHNlbGVjdCBkZXBfdGltZSwgZGVwX2RlbGF5LCBhcnJfdGltZSwgYW5kIGFycl9kZWxheSBmcm9tIGZsaWdodHMuCmBgYHtyfQpmbGlnaHRzICU+JSBzZWxlY3QoZGVwX3RpbWUsIGRlcF9kZWxheSwgYXJyX3RpbWUsIGFycl9kZWxheSkKCmBgYApgYGB7cn0KZmxpZ2h0cyAlPiUgc2VsZWN0KGNvbnRhaW5zKCJkZXAiKSwgY29udGFpbnMoImFyciIpKQoKYGBgCmBgYHtyfQpmbGlnaHRzICU+JSBzZWxlY3Qoc3RhcnRzX3dpdGgoImRlcCIpLCBzdGFydHNfd2l0aCgiYXJyIikpCgpgYGAKYGBge3J9CmZsaWdodHMgJT4lIHNlbGVjdChtYXRjaGVzKCJeKGRlcHxhcnIpXyh0aW1lfGRlbGF5KSIpKQoKYGBgCmBgYHtyfQp2YXJpYWJsZXMgPC0gYygiZGVwX3RpbWUiLCAiZGVwX2RlbGF5IiwgImFycl90aW1lIiwgImFycl9kZWxheSIpCmZsaWdodHMgJT4lIHNlbGVjdChhbGxfb2YodmFyaWFibGVzKSkKCmBgYApgYGB7cn0KZmxpZ2h0cyAlPiUgc2VsZWN0KDQsIDYsIDcsIDkpICAjIGRlcF90aW1lLCBkZXBfZGVsYXksIGFycl90aW1lLCBhcnJfZGVsYXkKCmBgYAojIyMjIDUuIFdoYXQgaGFwcGVucyBpZiB5b3UgaW5jbHVkZSB0aGUgbmFtZSBvZiBhIHZhcmlhYmxlIG11bHRpcGxlIHRpbWVzIGluIGEgc2VsZWN0KCkgY2FsbD8Kc2VsZWN0KCkg7ZWo7IiY7JeQ7IScIOuPmeydvO2VnCDrs4DsiJgg7J2066aE7J2EIOyXrOufrCDrsogg7Y+s7ZWo7ZWY66m0LCDqsrDqs7zsl5DripQg7ZW064u5IOuzgOyImOqwgCDtlZwg67KI66eMIOyEoO2DneuQnOuLpC4gIApkcGx5cuydgCBzZWxlY3QoKeyXkOyEnCDspJHrs7XrkJwg67OA7IiYIOydtOumhOydhCDsnpDrj5nsnLzroZwg7KCc6rGw7ZWY7JesLCDqsrDqs7zsl5Ag7KSR67O165CY7KeAIOyViuuPhOuhnSDsspjrpqztlZzri6QuICAKPGJyPgoKIyMjIyA2LiBXaGF0IGRvZXMgdGhlIGFueV9vZigpIGZ1bmN0aW9uIGRvPyBXaHkgbWlnaHQgaXQgYmUgaGVscGZ1bCBpbiBjb25qdW5jdGlvbiB3aXRoIHRoaXMgdmVjdG9yPwpgYGB7cn0KdmFyaWFibGVzIDwtIGMoInllYXIiLCAibW9udGgiLCAiZGF5IiwgImRlcF9kZWxheSIsICJhcnJfZGVsYXkiKQp2YXJpYWJsZXMKYGBgCmFueV9vZigp64qUIGRwbHly7J2YIO2XrO2NvCDtlajsiJjroZwsIOyngOygleuQnCDsubzrn7wg7J2066aEIOuqqeuhnSDspJEg7Iuk7KCcIOuNsOydtO2EsOyFi+yXkCDsobTsnqztlZjripQg7Lm865+866eMIOyEoO2Dne2VoCDsiJgg7J6I64+E66GdIO2VtOykgOuLpC4gIAoK7KaJLCDsp4DsoJXtlZwg7Lm865+8IOydtOumhCDspJEg7J2867aA6rCAIOuNsOydtO2EsOyFi+yXkCDsl4bslrTrj4Qg7Jik66WY66W8IOuwnOyDneyLnO2CpOyngCDslYrqs6AsIOyLpOygnOuhnCDsobTsnqztlZjripQg7Lm865+866eMIOyEoO2Dne2VnOuLpC4gIAoKICAgIGFueV9vZigp6rCAIOycoOyaqe2VnCDsnbTsnKA6CiAgICAgIOyjvOyWtOynhCDrsqHthLDqsIAg7JyE7J2YIOy9lOuTnOyZgCDqsJnsnYQg65WMLCAgc2VsZWN0KGZsaWdodHMsIGFueV9vZih2YXJpYWJsZXMpKeyZgCDqsJnsnbQg7IKs7Jqp7ZWY66m0IGZsaWdodHMg642w7J207YSw7IWL7JeQIO2PrO2VqOuQnCB2YXJpYWJsZXPsnZgg7Lm865+866eMIOyEoO2DneuQnOuLpC4g67Kh7YSw7JeQIOyeiOuKlCDrqqjrk6Ag7Lm865+87J20IGZsaWdodHMg642w7J207YSw7IWL7JeQIOyXhuyWtOuPhCDsmKTrpZjqsIAg67Cc7IOd7ZWY7KeAIOyViuycvOupsCwg642w7J207YSw7IWL7JeQIOyhtOyerO2VmOuKlCDsubzrn7zrp4wg7ISg7YOd65Cc64ukLiAKCuymiSwg7L2U65OcIOycoOyXsOyEsSwg7L2U65OcIOyerOyCrOyaqeyEsS4KPGJyPgoKIyMjIyA3LiBEb2VzIHRoZSByZXN1bHQgb2YgcnVubmluZyB0aGUgZm9sbG93aW5nIGNvZGUgc3VycHJpc2UgeW91PyBIb3cgZG8gdGhlIHNlbGVjdCBoZWxwZXJzIGRlYWwgd2l0aCBjYXNlIGJ5IGRlZmF1bHQ/IEhvdyBjYW4geW91IGNoYW5nZSB0aGF0IGRlZmF1bHQ/CmBgYHtyfQpzZWxlY3QoZmxpZ2h0cywgY29udGFpbnMoIlRJTUUiKSkgCmBgYApjb250YWlucygpIO2VqOyImOqwgCDrjIDshozrrLjsnpAg6rWs67aE7J20IOyViOuQnOuLpC4gICAgCuuMgOyGjOusuOyekOulvCDqtazrtoTtlZjsl6wg6rKA7IOJ7ZWY66Ck66m0ICoqaWdub3JlLmNhc2UgPSBGQUxTRSoqIOyYteyFmOydhCDstpTqsIDtlaAg7IiYIOyeiOuLpC4gCmBgYHtyfQpzZWxlY3QoZmxpZ2h0cywgY29udGFpbnMoIlRJTUUiLCBpZ25vcmUuY2FzZSA9IEZBTFNFKSkgCmBgYAojIyA1LjQuNi4gCiMjIyMgMS4gV2hpY2ggY2FycmllciBoYXMgdGhlIHdvcnN0IGRlbGF5cz8gQ2hhbGxlbmdlOiBjYW4geW91IGRpc2VudGFuZ2xlIHRoZSBlZmZlY3RzIG9mIGJhZCBhaXJwb3J0cyB2cy4gYmFkIGNhcnJpZXJzPyBXaHkvd2h5IG5vdD8gKEhpbnQ6IHRoaW5rIGFib3V0IGZsaWdodHMgJT4lIGdyb3VwX2J5KGNhcnJpZXIsIGRlc3QpICU+JSBzdW1tYXJpc2UobigpKSkKYGBge3J9CmNhcnJpZXJfZGVsYXlzIDwtIGZsaWdodHMgJT4lCiAgZ3JvdXBfYnkoY2FycmllcikgJT4lCiAgc3VtbWFyaXNlKGF2Z19kZXBfZGVsYXkgPSBtZWFuKGRlcF9kZWxheSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgYXJyYW5nZShkZXNjKGF2Z19kZXBfZGVsYXkpKQoKY2Fycmllcl9kZWxheXMKYGBgCndvcnN0IGRlbGF5czogKipGOSoqICAKPGJyPgoKIyMjIGNoYWxsZW5nZTpjYW4geW91IGRpc2VudGFuZ2xlIHRoZSBlZmZlY3RzIG9mIGJhZCBhaXJwb3J0cyB2cy4gYmFkIGNhcnJpZXJzPyBXaHkvd2h5IG5vdD8KYGBge3J9CmNhcnJpZXJfZGVzdF9jb3VudHMgPC0gZmxpZ2h0cyAlPiUKICBncm91cF9ieShjYXJyaWVyLCBkZXN0KSAlPiUKICBzdW1tYXJpc2UoY291bnQgPSBuKCksIGF2Z19kZXBfZGVsYXkgPSBtZWFuKGRlcF9kZWxheSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgYXJyYW5nZShkZXNjKGF2Z19kZXBfZGVsYXkpKQoKY2Fycmllcl9kZXN0X2NvdW50cwoKYGBgCgrtirnsoJUg7ZWt6rO17IKsLeqzte2VrSDsobDtlansl5DshJzsnZgg7Y+J6regIOyngOyXsCDsi5zqsITsnYQg7ZmV7J247ZWgIOyImCDsnojri6QuIO2VmOyngOunjCDsnbQg67Cp7Iud7Jy866GcIOqzte2VreqzvCDtla3qs7Xsgqwg6rCE7J2YIOyngOyXsCDsm5DsnbjsnYQg7JmE67K97ZWY6rKMIOq1rOu2hO2VmOuKlCDqsoPsnYAg7Ja066C164ukLiDsmIjrpbwg65Ok7Ja0LCDtla3qs7XquLAg7Iqk7LyA7KSE7J2064KYIO2KueyglSDsi5zqsITrjIDsnZgg7Zi87J6h64+ELCDrgqDslKgg65Ox7J2YIOuLpOuluCDsmpTshozrk6Trj4Qg7KeA7Jew7JeQIOyYge2WpeydhCDrr7jsuZjquLAg65WM66y47JeQLCDri6jsiJztnogg6rO17ZWt6rO8IO2VreqzteyCrOunjOydhCDruYTqtZDtlbTshJzripQg7KCE7LK07KCB7J24IOyngOyXsCDsm5DsnbjsnYQg66qF7ZmV7ZWY6rKMIOq1rOu2hO2VmOq4sCDslrTroLXri6QuIOyngOyXsCDsm5DsnbjsnYQg7JmE7KCE7Z6IIOydtO2VtO2VmOugpOuptCDrjZQg66eO7J2AIOuzgOyImOulvCDqs6DroKTtlbTslbwg7ZWc64ukLiAgCjxicj4KCiMjIyMgMiAuIFdoYXQgZG9lcyB0aGUgc29ydCBhcmd1bWVudCB0byBjb3VudCgpIGRvLiBDYW4geW91IGV4cGxhaW4gaXQgaW4gdGVybXMgb2YgdGhlIGRwbHlyIHZlcmJzIHlvdeKAmXZlIGxlYXJuZWQgc28gZmFyPwpjb3VudCgpIO2VqOyImOydmCBzb3J0ID0gVFJVRSDsnbjsiJjrpbwg7IKs7Jqp7ZWY66m0LCDqsrDqs7zqsIAg7J6Q64+Z7Jy866GcIOy5tOyatO2KuOqwgCDtgbAg7Iic7ISc64yA66GcIOygleugrOuQnOuLpC4KYGBge3J9CmNhcnJpZXJfY291bnRzIDwtIGZsaWdodHMgJT4lCiAgY291bnQoY2Fycmllciwgc29ydCA9IFRSVUUpCmNhcnJpZXJfY291bnRzCmBgYApzb3J0ID0gVFJVReuKlCBhcnJhbmdlKGRlc2Mobikp7JmAIOqwmeydgCDquLDriqXsnYQg7IiY7ZaJ7ZWc64uk6rOgIOunkO2VoCDsiJgg7J6I64ukLgpgYGB7cn0KY2Fycmllcl9jb3VudHMgPC0gZmxpZ2h0cyAlPiUKICBjb3VudChjYXJyaWVyKSAlPiUgICAgICAgICAKICBhcnJhbmdlKGRlc2MobikpICPrgrTrprzssKjsiJwg7KCV66CsCgpjYXJyaWVyX2NvdW50cwpgYGAKIyMgOS4zLjMKIyMjIyAxLiDtlYTrk5zqsIAg4oCcfOKAnSDroZwg67aE66as65CcIO2MjOydvOydhCDsnb3snLzroKTrqbQg7Ja065akIO2VqOyImOulvCDsgqzsmqntlZjqsqDripTqsIA/CnJlYWRfZGVsaW0oKQpgYGB7cn0KcmVhZF9kZWxpbSgiZGF0YS9zdHVkZW50cy5jc3YiLCBkZWxpbSA9ICJ8Iiwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkKYGBgCiMjIyMgMi4gcmVhZF9jc3YoKSDsmYAgcmVhZF90c3YoKSDqsIAg6rO17Ya17Jy866GcIOqwgOynhCDsnbjsiJjripQgZmlsZSwgc2tpcCwgY29tbWVudCDsmbjsl5Ag65iQIOustOyXh+ydtCDsnojripTqsIA/CmBgYHtyfQppbnRlcnNlY3QobmFtZXMoZm9ybWFscyhyZWFkX2NzdikpLCBuYW1lcyhmb3JtYWxzKHJlYWRfdHN2KSkpCmBgYAogIGNvbF9uYW1lc+yZgCBjb2xfdHlwZXM6IOyXtCDsnbTrpoTqs7wg7Je07J2EIOyWtOuWu+qyjCDqtazrrLgg67aE7ISd7ZWg7KeA66W8IOyngOyglSAgCiAgCiAgbG9jYWxlOiDsnbjsvZTrlKkg7ISk7KCV6rO8IOyGjOyImOygkCDqtazrtoQg6riw7Zi4KCIuIiDrmJDripQgIiwiKSDrk7HsnYQg6rKw7KCV7ZWY64qUIOuNsCDspJHsmpQgIAogIAogIG5h7JmAIHF1b3RlZF9uYTog6rKw7Lih6rCS7Jy866GcIOyymOumrO2VoCDrrLjsnpDsl7TsnYQg7KCc7Ja0ICAKICAKICB0cmltX3dzOiDshYAg7JWe65Kk7J2YIOqzteuwseydhCDsoJzqsbDtlZwg7ZuEIOq1rOusuCDrtoTshJ0gIAogIAogIG5fbWF4OiDsnb3snYQg7LWc64yAIO2WiSDsiJjrpbwg7ISk7KCVICAKICAKICBndWVzc19tYXg6IOyXtCDsnKDtmJXsnYQg7LaU7Lih7ZWgIOuVjCDsgqzsmqntlaAg7ZaJIOyImOulvCDshKTsoJUgIAogIAogIHByb2dyZXNzOiDsp4Ttlokg7ZGc7Iuc7KSE7J2EIO2RnOyLnO2VoOyngCDsl6zrtoDrpbwg6rKw7KCVICAKICAKICA8YnI+CiAgCiMjIyMgMy4gcmVhZF9md2YoKSDsl5DshJwg6rCA7J6lIOykkeyalO2VnCDsnbjsiJjripQg66y07JeH7J246rCAPwoKcmVhZF9md2YoKSDtlajsiJjsl5DshJwg6rCA7J6lIOykkeyalO2VnCDsnbjsiJjripQgY29sX3Bvc2l0aW9uc+ydtOuLpC4gIArsnbQg7J247IiY64qUIOuNsOydtO2EsCDsl7TsnbQg7Iuc7J6R7ZWY6rOgIOuBneuCmOuKlCDsnITsuZjrpbwg7ZWo7IiY7JeQIOyVjOugpOyjvOupsCwgImZpeGVkLXdpZHRoIGZvcm1hdHMi7J2YIO2MjOydvOydhCDsnb3snYQg65WMIOyCrOyaqeuQnOuLpC4gIAo8YnI+CgojIyMjIDQuIENTViDtjIzsnbzsnZgg66y47J6Q7Je07JeQIOyJvO2RnOqwgCDtj6ztlajrkJjripQg6rK97Jqw6rCAIOyeiOuLpC4g6re46rKD65Ok7J20IOusuOygnOulvCDsnbzsnLwg7YKk7KeAIOyViuqyjCDtlZjroKTrqbQgIiDtmLnsnYAgJ+yZgCDqsJnsnYAg7J247JqpIOusuOyekOuhnCDrkZjrn6zsi7jsnbwg7ZWE7JqU6rCAIOyeiOuLpC4gcmVhZF9jc3YoKSDripQg7J247JqpIOusuOyekOqwgCAi65286rOgIOqwgOygle2VnOuLpC4g7J2066W8IOuzgOqyve2VmOugpOuptCByZWFkX2RlbGltKCkg7J2EIOuMgOyLoCDsgqzsmqntlZjrqbQg65Cc64ukLiDri6TsnYwg7YWN7Iqk7Yq466W8IOuNsOydtO2EsO2UhOugiOyehOycvOuhnCDsnb3snLzroKTrqbQg7Ja065akIOyduOyImOulvCDshKTsoJXtlbTslbztlZjripTqsIA/CmBgYHtyfQoieCx5XG4xLCdhLGInIgoKYGBgCnJlYWRfZGVsaW0oKeydhCDsgqzsmqntlaAg6rK97JqwLCDqtazrtoQg66y47J6Q66GcICIsIuulvCDsp4DsoJXtlZjqs6AgcXVvdGUg7J247IiY66W8IOyEpOygle2VtOyVvCDtlZzri6QuCmBgYHtyfQp4IDwtICJ4LHlcbjEsJ2EsYiciCnJlYWRfZGVsaW0oeCwgIiwiLCBxdW90ZSA9ICInIiwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkKYGBgCuuYkO2VnCByZWFkX2Nzdigp64qUIHF1b3RlIOyduOyImOulvCDsp4Dsm5DtlZzri6QuCgpgYGB7cn0KcmVhZF9jc3YoeCwgcXVvdGUgPSAiJyIsIHNob3dfY29sX3R5cGVzID0gRkFMU0UpCmBgYAoKIyMjIyA1LiDri6TsnYwg6rCBIOyduOudvOyduCBDU1Yg7YyM7J287JeQIOyWtOuWpCDrrLjsoJzqsIAg7J6I64qU7KeAIO2ZleyduO2VmOudvC4g7L2U65Oc66W8IOyLpO2Wie2VmOuptCDslrTrlrvqsowg65CY64qU6rCAPwpgYGB7cn0KIzEKcmVhZF9jc3YoImEsYlxuMSwyLDNcbjQsNSw2IikKIz4gV2FybmluZzogT25lIG9yIG1vcmUgcGFyc2luZyBpc3N1ZXMsIHNlZSBgcHJvYmxlbXMoKWAgZm9yIGRldGFpbHMKIz4gUm93czogMiBDb2x1bW5zOiAyCiM+IOKUgOKUgCBDb2x1bW4gc3BlY2lmaWNhdGlvbiDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKIz4gRGVsaW1pdGVyOiAiLCIKIz4gZGJsICgxKTogYQojPiAKIz4g4oS5IFVzZSBgc3BlYygpYCB0byByZXRyaWV2ZSB0aGUgZnVsbCBjb2x1bW4gc3BlY2lmaWNhdGlvbiBmb3IgdGhpcyBkYXRhLgojPiDihLkgU3BlY2lmeSB0aGUgY29sdW1uIHR5cGVzIG9yIHNldCBgc2hvd19jb2xfdHlwZXMgPSBGQUxTRWAgdG8gcXVpZXQgdGhpcyBtZXNzYWdlLgojPiAjIEEgdGliYmxlOiAyIMOXIDIKIz4gICAgICAgYSAgICAgYgojPiAgIDxkYmw+IDxkYmw+CiM+IDEgICAgIDEgICAgMjMKIz4gMiAgICAgNCAgICA1NgpgYGAK65GQIOqwnOydmCDsl7QoYeyZgCBiKeunjCDsp4DsoJXrkJjslrQg7J6I7KeA66eMLCDqsIEg7ZaJ7JeQ64qUIOyEuCDqsJzsnZgg6rCS7J20IOyeiOyKteuLiOuLpC4g6re4656Y7IScIOuniOyngOuniSDsl7Qg6rCS7J20IOyemOumsOuLpC4KYGBge3J9CiMyCnJlYWRfY3N2KCJhLGIsY1xuMSwyXG4xLDIsMyw0IikKIz4gV2FybmluZzogT25lIG9yIG1vcmUgcGFyc2luZyBpc3N1ZXMsIHNlZSBgcHJvYmxlbXMoKWAgZm9yIGRldGFpbHMKIz4gUm93czogMiBDb2x1bW5zOiAzCiM+IOKUgOKUgCBDb2x1bW4gc3BlY2lmaWNhdGlvbiDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKIz4gRGVsaW1pdGVyOiAiLCIKIz4gZGJsICgyKTogYSwgYgojPiAKIz4g4oS5IFVzZSBgc3BlYygpYCB0byByZXRyaWV2ZSB0aGUgZnVsbCBjb2x1bW4gc3BlY2lmaWNhdGlvbiBmb3IgdGhpcyBkYXRhLgojPiDihLkgU3BlY2lmeSB0aGUgY29sdW1uIHR5cGVzIG9yIHNldCBgc2hvd19jb2xfdHlwZXMgPSBGQUxTRWAgdG8gcXVpZXQgdGhpcyBtZXNzYWdlLgojPiAjIEEgdGliYmxlOiAyIMOXIDMKIz4gICAgICAgYSAgICAgYiAgICAgYwojPiAgIDxkYmw+IDxkYmw+IDxkYmw+CiM+IDEgICAgIDEgICAgIDIgICAgTkEKIz4gMiAgICAgMSAgICAgMiAgICAzNApgYGAK7Zek642U7JeQ64qUIOyEuCDqsJzsnZgg7Je07J20IOyeiOyngOunjCwg7LKrIOuyiOynuCDtlonsl5DripQg65GQIOqwnOydmCDqsJLrp4wg7J6I6rOgIOuRkCDrsojsp7gg7ZaJ7JeQ64qUIOuEpCDqsJzsnZgg6rCS7J20IOyeiOuLpC4g7LKrIOuyiOynuCDtlonsl5DshJzripQgYyDsl7TsnbQg6rKw7Lih7LmY66GcIOyEpOygleuQmOqzoCwg65GQIOuyiOynuCDtlonsl5DshJzripQg7LaU6rCA65CcIOqwkuydtCDsnpjrprDri6QuCmBgYHtyfQojMwpyZWFkX2NzdigiYSxiXG5cIjEiKQojPiBSb3dzOiAwIENvbHVtbnM6IDIKIz4g4pSA4pSAIENvbHVtbiBzcGVjaWZpY2F0aW9uIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAojPiBEZWxpbWl0ZXI6ICIsIgojPiBjaHIgKDIpOiBhLCBiCiM+IAojPiDihLkgVXNlIGBzcGVjKClgIHRvIHJldHJpZXZlIHRoZSBmdWxsIGNvbHVtbiBzcGVjaWZpY2F0aW9uIGZvciB0aGlzIGRhdGEuCiM+IOKEuSBTcGVjaWZ5IHRoZSBjb2x1bW4gdHlwZXMgb3Igc2V0IGBzaG93X2NvbF90eXBlcyA9IEZBTFNFYCB0byBxdWlldCB0aGlzIG1lc3NhZ2UuCiM+ICMgQSB0aWJibGU6IDAgw5cgMgojPiAjIOKApiB3aXRoIDIgdmFyaWFibGVzOiBhIDxjaHI+LCBiIDxjaHI+CmBgYAox7J20652864qUIOyXtCDqsJLsnbQg7Je066a8IO2RnOyLnOunjCDsnojqs6Ag64ur7Z6I7KeAIOyViuyVmOq4sCDrlYzrrLjsl5Ag7J2Y64+E7ZWcIOuwlOqwgCDrtojrtoTrqoXtlZjri6QuIOyduOyaqeydtCDri6vtnojsp4Ag7JWK7JWEIGHripQg7Iir7J6Q7ZiV7Jy866GcIOyymOumrOuQmOqzoCwgYuuKlCDqsrDsuKHsuZjroZwg7ISk7KCV65Cc64ukLgpgYGB7cn0KIzQKcmVhZF9jc3YoImEsYlxuMSwyXG5hLGIiKQojPiBSb3dzOiAyIENvbHVtbnM6IDIKIz4g4pSA4pSAIENvbHVtbiBzcGVjaWZpY2F0aW9uIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAojPiBEZWxpbWl0ZXI6ICIsIgojPiBjaHIgKDIpOiBhLCBiCiM+IAojPiDihLkgVXNlIGBzcGVjKClgIHRvIHJldHJpZXZlIHRoZSBmdWxsIGNvbHVtbiBzcGVjaWZpY2F0aW9uIGZvciB0aGlzIGRhdGEuCiM+IOKEuSBTcGVjaWZ5IHRoZSBjb2x1bW4gdHlwZXMgb3Igc2V0IGBzaG93X2NvbF90eXBlcyA9IEZBTFNFYCB0byBxdWlldCB0aGlzIG1lc3NhZ2UuCiM+ICMgQSB0aWJibGU6IDIgw5cgMgojPiAgIGEgICAgIGIgICAgCiM+ICAgPGNocj4gPGNocj4KIz4gMSAxICAgICAyICAgIAojPiAyIGEgICAgIGIKYGBgCgoiYSLsmYAgImIi6rCAIOyIq+yekOqwgCDslYTri4wg66y47J6Q7Je07J2EIO2PrO2VqO2VmOqzoCDsnojsnLzrr4DroZwg66y47J6Q7ZiV7Jy866GcIOyymOumrOuQnOuLpC4g7J206rKD7J20IOydmOuPhOuQnCDqsoPsnbwg7IiY64+EIOyeiOqzoCwgMSwy7JmAIGEsYuqwgCDqsJLsnLzroZwg7KeA7KCV65CY6ri4IOybkO2WiOuNmCDqsoPsnbwg7IiY64+EIOyeiOuLpC4KYGBge3J9CiM1CnJlYWRfY3N2KCJhO2JcbjE7MyIpCiM+IFJvd3M6IDEgQ29sdW1uczogMQojPiDilIDilIAgQ29sdW1uIHNwZWNpZmljYXRpb24g4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiM+IERlbGltaXRlcjogIiwiCiM+IGNociAoMSk6IGE7YgojPiAKIz4g4oS5IFVzZSBgc3BlYygpYCB0byByZXRyaWV2ZSB0aGUgZnVsbCBjb2x1bW4gc3BlY2lmaWNhdGlvbiBmb3IgdGhpcyBkYXRhLgojPiDihLkgU3BlY2lmeSB0aGUgY29sdW1uIHR5cGVzIG9yIHNldCBgc2hvd19jb2xfdHlwZXMgPSBGQUxTRWAgdG8gcXVpZXQgdGhpcyBtZXNzYWdlLgojPiAjIEEgdGliYmxlOiAxIMOXIDEKIz4gICBgYTtiYAojPiAgIDxjaHI+CiM+IDEgMTszCmBgYArrjbDsnbTthLDqsIAgIjsi66GcIOq1rOu2hOuQmOyWtCDsnojri6QuIOydtCDqsr3smrAgcmVhZF9jc3YyKCnrpbwg7IKs7Jqp7ZWY66m0IOuNlCDsoIHtlantlZjri6QuCg==